├── ROADMAP.md
├── public
├── favicon.ico
├── og-image.png
├── media
│ ├── logo.webp
│ ├── meadow.webp
│ ├── surreal.webp
│ ├── gaiaui_logo.png
│ ├── logo_black.webp
│ ├── text_w_logo_white.webp
│ ├── wallpapers
│ │ ├── meadow.webp
│ │ └── surreal.webp
│ └── experience logo.svg
├── favicon-16x16.png
├── favicon-32x32.png
├── apple-touch-icon.png
├── android-chrome-192x192.png
├── android-chrome-512x512.png
├── site.webmanifest
└── r
│ ├── goal-card.json
│ ├── todo-item.json
│ ├── cli-command.json
│ ├── file-dropzone.json
│ ├── twitter-card.json
│ ├── model-selector.json
│ ├── code-block.json
│ ├── component-preview-tooltip.json
│ ├── file-preview.json
│ ├── workflow-card.json
│ ├── notification-card.json
│ ├── calendar-event-card.json
│ ├── github-stars-button.json
│ └── author-tooltip.json
├── .npmrc
├── postcss.config.mjs
├── components
├── previews
│ ├── navbar-menu
│ │ ├── default.tsx
│ │ ├── static-links.tsx
│ │ └── basic.tsx
│ ├── github-stars-button
│ │ ├── default.tsx
│ │ ├── sizes.tsx
│ │ └── variants.tsx
│ ├── raised-button
│ │ ├── sizes.tsx
│ │ └── default.tsx
│ ├── code-block
│ │ ├── default.tsx
│ │ ├── line-numbers.tsx
│ │ └── languages.tsx
│ ├── author-tooltip
│ │ ├── default.tsx
│ │ └── sizes.tsx
│ ├── file-dropzone
│ │ └── default.tsx
│ ├── message-bubble
│ │ ├── simple.tsx
│ │ ├── grouped.tsx
│ │ ├── default.tsx
│ │ └── grouped-rounded.tsx
│ ├── email-compose-card
│ │ └── default.tsx
│ ├── twitter-card
│ │ └── default.tsx
│ ├── file-preview
│ │ ├── uploading.tsx
│ │ └── default.tsx
│ ├── todo-item
│ │ ├── priorities.tsx
│ │ └── default.tsx
│ ├── component-preview-tooltip
│ │ └── default.tsx
│ ├── index.tsx
│ ├── calendar-event-card
│ │ ├── default.tsx
│ │ └── actions.tsx
│ ├── weather-card
│ │ ├── default.tsx
│ │ ├── units.tsx
│ │ └── with-forecast.tsx
│ ├── holo-card
│ │ ├── default.tsx
│ │ └── colors.tsx
│ ├── search-results-tabs
│ │ ├── with-image-click.tsx
│ │ ├── web-only.tsx
│ │ ├── news.tsx
│ │ └── default.tsx
│ ├── link-preview
│ │ └── default.tsx
│ ├── composer
│ │ ├── with-files.tsx
│ │ └── default.tsx
│ ├── model-selector
│ │ └── default.tsx
│ ├── goal-card
│ │ └── default.tsx
│ ├── notification-card
│ │ └── default.tsx
│ ├── slash-command-dropdown
│ │ └── default.tsx
│ └── nested-menu
│ │ └── default.tsx
├── icons
│ ├── index.ts
│ └── github.tsx
├── core
│ ├── navbar-wrapper.tsx
│ ├── docs-sidebar.tsx
│ ├── command-menu.tsx
│ ├── page-navigation.tsx
│ ├── copy-button.tsx
│ ├── code-block.tsx
│ ├── component-preview.tsx
│ ├── fullscreen-button.tsx
│ ├── source-code.tsx
│ ├── table-of-contents.tsx
│ ├── component-preview-client.tsx
│ ├── doc-page-layout.tsx
│ └── page-navigation-client.tsx
└── ui
│ ├── skeleton.tsx
│ ├── label.tsx
│ ├── separator.tsx
│ ├── kbd.tsx
│ ├── theme-toggle.tsx
│ ├── input.tsx
│ ├── avatar.tsx
│ ├── toggle.tsx
│ ├── badge.tsx
│ ├── popover.tsx
│ ├── scroll-area.tsx
│ ├── tooltip.tsx
│ ├── tabs.tsx
│ ├── card.tsx
│ ├── button.tsx
│ └── footer.tsx
├── lib
├── utils.ts
├── source.ts
├── siteConfig.ts
├── mdx.ts
└── navigation.ts
├── types
└── nav-item.ts
├── .env.example
├── registry
└── new-york
│ └── ui
│ ├── icons.tsx
│ ├── weather-detail-item.tsx
│ ├── todo-item.tsx
│ ├── github-stars-button.tsx
│ ├── message-bubble.tsx
│ ├── message-bubble.css
│ ├── raised-button.tsx
│ └── author-tooltip.tsx
├── content
└── docs
│ ├── contributors.mdx
│ ├── roadmap.mdx
│ ├── components.mdx
│ ├── index.mdx
│ ├── components
│ ├── raised-button.mdx
│ ├── file-dropzone.mdx
│ ├── twitter-card.mdx
│ ├── goal-card.mdx
│ ├── email-compose-card.mdx
│ ├── component-preview-tooltip.mdx
│ ├── model-selector.mdx
│ ├── calendar-event-card.mdx
│ ├── file-preview.mdx
│ ├── notification-card.mdx
│ ├── todo-item.mdx
│ ├── github-stars-button.mdx
│ ├── workflow-card.mdx
│ └── author-tooltip.mdx
│ └── installation.mdx
├── app
├── docs
│ └── layout.tsx
├── robots.ts
├── manifest.ts
├── layout.tsx
└── sitemap.ts
├── components.json
├── .gitignore
├── tsconfig.json
├── biome.json
├── next.config.ts
├── package.json
└── README.md
/ROADMAP.md:
--------------------------------------------------------------------------------
1 | [Roadmap](https://ui.heygaia.io/docs/roadmap)
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theexperiencecompany/ui/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/public/og-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theexperiencecompany/ui/HEAD/public/og-image.png
--------------------------------------------------------------------------------
/public/media/logo.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theexperiencecompany/ui/HEAD/public/media/logo.webp
--------------------------------------------------------------------------------
/public/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theexperiencecompany/ui/HEAD/public/favicon-16x16.png
--------------------------------------------------------------------------------
/public/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theexperiencecompany/ui/HEAD/public/favicon-32x32.png
--------------------------------------------------------------------------------
/public/media/meadow.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theexperiencecompany/ui/HEAD/public/media/meadow.webp
--------------------------------------------------------------------------------
/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theexperiencecompany/ui/HEAD/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/public/media/surreal.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theexperiencecompany/ui/HEAD/public/media/surreal.webp
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | @hugeicons-pro:registry=https://npm.hugeicons.com/
2 | //npm.hugeicons.com/:_authToken=${HUGEICONS_LICENSE_KEY}
--------------------------------------------------------------------------------
/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | const config = {
2 | plugins: ["@tailwindcss/postcss"],
3 | };
4 |
5 | export default config;
6 |
--------------------------------------------------------------------------------
/public/media/gaiaui_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theexperiencecompany/ui/HEAD/public/media/gaiaui_logo.png
--------------------------------------------------------------------------------
/public/media/logo_black.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theexperiencecompany/ui/HEAD/public/media/logo_black.webp
--------------------------------------------------------------------------------
/components/previews/navbar-menu/default.tsx:
--------------------------------------------------------------------------------
1 | import BasicNavbar from "./basic";
2 |
3 | export default BasicNavbar;
4 |
--------------------------------------------------------------------------------
/public/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theexperiencecompany/ui/HEAD/public/android-chrome-192x192.png
--------------------------------------------------------------------------------
/public/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theexperiencecompany/ui/HEAD/public/android-chrome-512x512.png
--------------------------------------------------------------------------------
/public/media/text_w_logo_white.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theexperiencecompany/ui/HEAD/public/media/text_w_logo_white.webp
--------------------------------------------------------------------------------
/public/media/wallpapers/meadow.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theexperiencecompany/ui/HEAD/public/media/wallpapers/meadow.webp
--------------------------------------------------------------------------------
/public/media/wallpapers/surreal.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theexperiencecompany/ui/HEAD/public/media/wallpapers/surreal.webp
--------------------------------------------------------------------------------
/lib/utils.ts:
--------------------------------------------------------------------------------
1 | import { type ClassValue, clsx } from "clsx";
2 | import { twMerge } from "tailwind-merge";
3 |
4 | export function cn(...inputs: ClassValue[]) {
5 | return twMerge(clsx(inputs));
6 | }
7 |
--------------------------------------------------------------------------------
/types/nav-item.ts:
--------------------------------------------------------------------------------
1 | export interface NavItem {
2 | title: string;
3 | href: string;
4 | icon?: string;
5 | }
6 |
7 | export interface NavSection {
8 | title: string;
9 | items: NavItem[];
10 | }
11 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | # ===== Hugeicons =====
2 | # Set to "true" to use Pro (solid) icons instead of Free (stroke) icons
3 | # Requires HUGEICONS_LICENSE_KEY to be set in .npmrc for Pro package installation
4 | USE_PRO_ICONS=false
--------------------------------------------------------------------------------
/registry/new-york/ui/icons.tsx:
--------------------------------------------------------------------------------
1 | // registry/new-york/ui/icons.tsx
2 | // Free icons - upgrade to Pro by changing the import below
3 | export * from "@hugeicons/core-free-icons";
4 | export { HugeiconsIcon } from "@hugeicons/react";
5 |
--------------------------------------------------------------------------------
/content/docs/contributors.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Contributors"
3 | description: "Thank you to everyone who has contributed to GAIA UI!"
4 | ---
5 |
6 | import { ContributorsPage } from "@/components/core/contributors-page"
7 |
8 |
9 |
--------------------------------------------------------------------------------
/components/icons/index.ts:
--------------------------------------------------------------------------------
1 | // components/icons/index.ts
2 | // Uses free icons by default
3 | // When USE_PRO_ICONS=true, webpack swaps to Pro icons at build time
4 |
5 | export * from "@hugeicons/core-free-icons";
6 | export { HugeiconsIcon } from "@hugeicons/react";
7 |
--------------------------------------------------------------------------------
/components/core/navbar-wrapper.tsx:
--------------------------------------------------------------------------------
1 | import { Navbar } from "@/components/ui/navbar";
2 | import { getNavigation } from "@/lib/navigation";
3 |
4 | export function NavbarWrapper() {
5 | const navigation = getNavigation();
6 |
7 | return ;
8 | }
9 |
--------------------------------------------------------------------------------
/components/core/docs-sidebar.tsx:
--------------------------------------------------------------------------------
1 | import { getNavigation } from "@/lib/navigation";
2 | import { DocsSidebarClient } from "./docs-sidebar-client";
3 |
4 | export function DocsSidebar() {
5 | const navigation = getNavigation();
6 |
7 | return ;
8 | }
9 |
--------------------------------------------------------------------------------
/components/ui/skeleton.tsx:
--------------------------------------------------------------------------------
1 | import { cn } from "@/lib/utils";
2 |
3 | function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
4 | return (
5 |
10 | );
11 | }
12 |
13 | export { Skeleton };
14 |
--------------------------------------------------------------------------------
/components/previews/github-stars-button/default.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { GitHubStarsButton } from "@/registry/new-york/ui/github-stars-button";
4 |
5 | export default function GitHubStarsButtonDefault() {
6 | return (
7 |
8 |
9 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/components/previews/raised-button/sizes.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { RaisedButton } from "@/registry/new-york/ui/raised-button";
4 |
5 | export default function RaisedButtonSizes() {
6 | return (
7 | <>
8 | Small
9 | Default
10 | Large
11 | >
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/components/previews/navbar-menu/static-links.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { NavbarWithMenu } from "@/registry/new-york/ui/navbar-menu";
4 |
5 | export default function NavbarMenuStaticLinks() {
6 | return (
7 |
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/public/site.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "name": "",
3 | "short_name": "",
4 | "icons": [
5 | {
6 | "src": "/android-chrome-192x192.png",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | },
10 | {
11 | "src": "/android-chrome-512x512.png",
12 | "sizes": "512x512",
13 | "type": "image/png"
14 | }
15 | ],
16 | "theme_color": "#ffffff",
17 | "background_color": "#ffffff",
18 | "display": "standalone"
19 | }
20 |
--------------------------------------------------------------------------------
/components/core/command-menu.tsx:
--------------------------------------------------------------------------------
1 | import type { NavSection } from "@/types/nav-item";
2 | import { CommandMenuClient } from "./command-menu-client";
3 |
4 | interface CommandMenuProps {
5 | open: boolean;
6 | setOpen: (open: boolean) => void;
7 | navigation: NavSection[];
8 | }
9 |
10 | export function CommandMenu({ open, setOpen, navigation }: CommandMenuProps) {
11 | return (
12 |
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/components/previews/github-stars-button/sizes.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { GitHubStarsButton } from "@/registry/new-york/ui/github-stars-button";
4 |
5 | export default function GitHubStarsButtonSizes() {
6 | return (
7 |
8 |
9 |
10 |
11 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/public/r/goal-card.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3 | "name": "goal-card",
4 | "type": "registry:ui",
5 | "title": "Goal Card",
6 | "description": "A card component for displaying goal progress with status indicators and step tracking.",
7 | "dependencies": ["lucide-react"],
8 | "registryDependencies": [],
9 | "files": [
10 | {
11 | "path": "registry/new-york/ui/goal-card.tsx",
12 | "type": "registry:ui"
13 | }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/public/r/todo-item.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3 | "name": "todo-item",
4 | "type": "registry:ui",
5 | "title": "Todo Item",
6 | "description": "An interactive todo item with priority colors, due dates, labels, and subtask support.",
7 | "dependencies": ["lucide-react"],
8 | "registryDependencies": [],
9 | "files": [
10 | {
11 | "path": "registry/new-york/ui/todo-item.tsx",
12 | "type": "registry:ui"
13 | }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/app/docs/layout.tsx:
--------------------------------------------------------------------------------
1 | import { DocsSidebar } from "@/components/core/docs-sidebar";
2 |
3 | export default function DocsLayout({
4 | children,
5 | }: {
6 | children: React.ReactNode;
7 | }) {
8 | return (
9 |
10 |
11 |
12 | {children}
13 |
14 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/public/r/cli-command.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3 | "name": "cli-command",
4 | "type": "registry:ui",
5 | "title": "CLI Command",
6 | "description": "A command display with tabs for different package managers and copy functionality.",
7 | "dependencies": ["lucide-react"],
8 | "registryDependencies": [],
9 | "files": [
10 | {
11 | "path": "registry/new-york/ui/cli-command.tsx",
12 | "type": "registry:ui"
13 | }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/public/r/file-dropzone.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3 | "name": "file-dropzone",
4 | "type": "registry:ui",
5 | "title": "File Dropzone",
6 | "description": "A drag-and-drop file upload zone with preview, validation, and file management.",
7 | "dependencies": ["lucide-react"],
8 | "registryDependencies": [],
9 | "files": [
10 | {
11 | "path": "registry/new-york/ui/file-dropzone.tsx",
12 | "type": "registry:ui"
13 | }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/public/r/twitter-card.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3 | "name": "twitter-card",
4 | "type": "registry:ui",
5 | "title": "Twitter Card",
6 | "description": "A Twitter/X-style post card with author info, engagement metrics, and media support.",
7 | "dependencies": ["lucide-react"],
8 | "registryDependencies": [],
9 | "files": [
10 | {
11 | "path": "registry/new-york/ui/twitter-card.tsx",
12 | "type": "registry:ui"
13 | }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/components.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema.json",
3 | "style": "new-york",
4 | "rsc": true,
5 | "tsx": true,
6 | "tailwind": {
7 | "config": "",
8 | "css": "app/globals.css",
9 | "baseColor": "neutral",
10 | "cssVariables": true,
11 | "prefix": ""
12 | },
13 | "aliases": {
14 | "components": "@/components",
15 | "utils": "@/lib/utils",
16 | "ui": "@/components/ui",
17 | "lib": "@/lib",
18 | "hooks": "@/hooks"
19 | },
20 | "iconLibrary": "lucide"
21 | }
22 |
--------------------------------------------------------------------------------
/public/r/model-selector.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3 | "name": "model-selector",
4 | "type": "registry:ui",
5 | "title": "Model Selector",
6 | "description": "A dropdown selector for choosing AI models with provider information and pro badges.",
7 | "dependencies": ["lucide-react"],
8 | "registryDependencies": [],
9 | "files": [
10 | {
11 | "path": "registry/new-york/ui/model-selector.tsx",
12 | "type": "registry:ui"
13 | }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/public/r/code-block.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3 | "name": "code-block",
4 | "type": "registry:ui",
5 | "title": "Code Block",
6 | "description": "A syntax-highlighted code block with copy and download functionality, perfect for displaying code snippets.",
7 | "dependencies": ["lucide-react"],
8 | "registryDependencies": [],
9 | "files": [
10 | {
11 | "path": "registry/new-york/ui/code-block.tsx",
12 | "type": "registry:ui"
13 | }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/public/r/component-preview-tooltip.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3 | "name": "component-preview-tooltip",
4 | "type": "registry:ui",
5 | "title": "Component Preview Tooltip",
6 | "description": "A hover tooltip that shows a live preview of any component.",
7 | "dependencies": [],
8 | "registryDependencies": [],
9 | "files": [
10 | {
11 | "path": "registry/new-york/ui/component-preview-tooltip.tsx",
12 | "type": "registry:ui"
13 | }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/public/r/file-preview.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3 | "name": "file-preview",
4 | "type": "registry:ui",
5 | "title": "File Preview",
6 | "description": "Display uploaded files with smart type detection, file icons, image thumbnails, and upload progress.",
7 | "dependencies": ["lucide-react"],
8 | "registryDependencies": [],
9 | "files": [
10 | {
11 | "path": "registry/new-york/ui/file-preview.tsx",
12 | "type": "registry:ui"
13 | }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/components/previews/raised-button/default.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { RaisedButton } from "@/registry/new-york/ui/raised-button";
4 |
5 | export default function RaisedButtonDefault() {
6 | return (
7 | <>
8 | Default
9 | Blue
10 | Red
11 | Green
12 | Purple
13 | >
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/public/r/workflow-card.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3 | "name": "workflow-card",
4 | "type": "registry:ui",
5 | "title": "Workflow Card",
6 | "description": "A card for displaying workflow automations with rotated tool icons, execution stats, and action buttons.",
7 | "dependencies": ["lucide-react"],
8 | "registryDependencies": [],
9 | "files": [
10 | {
11 | "path": "registry/new-york/ui/workflow-card.tsx",
12 | "type": "registry:ui"
13 | }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/public/r/notification-card.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3 | "name": "notification-card",
4 | "type": "registry:ui",
5 | "title": "Notification Card",
6 | "description": "An enhanced notification card with action buttons, read/unread states, and timestamp display.",
7 | "dependencies": ["lucide-react"],
8 | "registryDependencies": [],
9 | "files": [
10 | {
11 | "path": "registry/new-york/ui/notification-card.tsx",
12 | "type": "registry:ui"
13 | }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/public/r/calendar-event-card.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3 | "name": "calendar-event-card",
4 | "type": "registry:ui",
5 | "title": "Calendar Event Card",
6 | "description": "A styled card for displaying calendar events with color indicators, status states, and action buttons.",
7 | "dependencies": ["lucide-react"],
8 | "registryDependencies": [],
9 | "files": [
10 | {
11 | "path": "registry/new-york/ui/calendar-event-card.tsx",
12 | "type": "registry:ui"
13 | }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/components/previews/code-block/default.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { CodeBlock } from "@/registry/new-york/ui/code-block";
4 |
5 | export default function CodeBlockDefault() {
6 | const code = `function greet(name) {
7 | console.log(\`Hello, \${name}!\`);
8 | return \`Welcome to GAIA UI\`;
9 | }
10 |
11 | greet("Developer");`;
12 |
13 | return (
14 |
15 |
16 | {code}
17 |
18 |
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/components/previews/author-tooltip/default.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { AuthorTooltip } from "@/registry/new-york/ui/author-tooltip";
4 |
5 | export default function AuthorTooltipDefault() {
6 | return (
7 |
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/components/core/page-navigation.tsx:
--------------------------------------------------------------------------------
1 | import type { NavSection } from "@/types/nav-item";
2 | import { PageNavigationClient } from "./page-navigation-client";
3 |
4 | interface PageNavigationProps {
5 | position?: "top" | "bottom";
6 | markdownContent?: string;
7 | navigation: NavSection[];
8 | }
9 |
10 | export function PageNavigation({
11 | position = "top",
12 | markdownContent,
13 | navigation,
14 | }: PageNavigationProps) {
15 | return (
16 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/components/previews/file-dropzone/default.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import {
4 | type DroppedFile,
5 | FileDropzone,
6 | } from "@/registry/new-york/ui/file-dropzone";
7 |
8 | export default function FileDropzoneDefault() {
9 | const handleFilesDropped = (files: DroppedFile[]) => {
10 | console.log("Files dropped:", files);
11 | };
12 |
13 | return (
14 |
15 |
21 |
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/components/previews/github-stars-button/variants.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { GitHubStarsButton } from "@/registry/new-york/ui/github-stars-button";
4 |
5 | export default function GitHubStarsButtonVariants() {
6 | return (
7 |
8 |
12 |
13 |
Without Label
14 |
15 |
16 |
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.*
7 | .yarn/*
8 | !.yarn/patches
9 | !.yarn/plugins
10 | !.yarn/releases
11 | !.yarn/versions
12 |
13 | # testing
14 | /coverage
15 |
16 | # next.js
17 | /.next/
18 | /out/
19 |
20 | # production
21 | /build
22 |
23 | # misc
24 | .DS_Store
25 | *.pem
26 |
27 | # debug
28 | npm-debug.log*
29 | yarn-debug.log*
30 | yarn-error.log*
31 | .pnpm-debug.log*
32 |
33 | # env files (can opt-in for committing if needed)
34 | .env
35 |
36 | # vercel
37 | .vercel
38 |
39 | # typescript
40 | *.tsbuildinfo
41 | next-env.d.ts
42 |
--------------------------------------------------------------------------------
/components/ui/label.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as LabelPrimitive from "@radix-ui/react-label";
4 | import type * as React from "react";
5 |
6 | import { cn } from "@/lib/utils";
7 |
8 | function Label({
9 | className,
10 | ...props
11 | }: React.ComponentProps) {
12 | return (
13 |
21 | );
22 | }
23 |
24 | export { Label };
25 |
--------------------------------------------------------------------------------
/components/previews/message-bubble/simple.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { MessageBubble } from "@/registry/new-york/ui/message-bubble";
4 |
5 | export default function MessageBubbleSimple() {
6 | return (
7 |
8 |
9 |
13 |
14 |
18 |
22 |
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2017",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "noEmit": true,
9 | "esModuleInterop": true,
10 | "module": "esnext",
11 | "moduleResolution": "bundler",
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "jsx": "react-jsx",
15 | "incremental": true,
16 | "plugins": [
17 | {
18 | "name": "next"
19 | }
20 | ],
21 | "paths": {
22 | "@/*": ["./*"]
23 | }
24 | },
25 | "include": [
26 | "next-env.d.ts",
27 | "**/*.ts",
28 | "**/*.tsx",
29 | ".next/types/**/*.ts",
30 | ".next/dev/types/**/*.ts"
31 | ],
32 | "exclude": ["node_modules"]
33 | }
34 |
--------------------------------------------------------------------------------
/lib/source.ts:
--------------------------------------------------------------------------------
1 | import path from "node:path";
2 | import fs from "fs";
3 |
4 | /**
5 | * Read source code from a file in the project
6 | * @param filePath - Path relative to project root (e.g., "registry/new-york/ui/raised-button.tsx")
7 | * @returns The file contents as a string
8 | */
9 | export function getSourceCode(filePath: string): string {
10 | const fullPath = path.join(process.cwd(), filePath);
11 |
12 | if (!fs.existsSync(fullPath)) {
13 | console.warn(`Source file not found: ${filePath}`);
14 | return `// File not found: ${filePath}`;
15 | }
16 |
17 | try {
18 | return fs.readFileSync(fullPath, "utf8");
19 | } catch (error) {
20 | console.error(`Error reading source file: ${filePath}`, error);
21 | return `// Error reading file: ${filePath}`;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://biomejs.dev/schemas/2.3.7/schema.json",
3 | "vcs": {
4 | "enabled": true,
5 | "clientKind": "git",
6 | "useIgnoreFile": true
7 | },
8 | "files": {
9 | "ignoreUnknown": false
10 | },
11 | "formatter": {
12 | "enabled": true,
13 | "indentStyle": "tab"
14 | },
15 | "linter": {
16 | "domains": {
17 | "next": "recommended"
18 | },
19 | "enabled": true,
20 | "rules": {
21 | "recommended": true
22 | }
23 | },
24 | "javascript": {
25 | "formatter": {
26 | "quoteStyle": "double"
27 | }
28 | },
29 | "css": {
30 | "parser": {
31 | "tailwindDirectives": true
32 | }
33 | },
34 | "assist": {
35 | "enabled": true,
36 | "actions": {
37 | "source": {
38 | "organizeImports": "on"
39 | }
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/components/previews/email-compose-card/default.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { EmailComposeCard } from "@/registry/new-york/ui/email-compose-card";
4 |
5 | export default function EmailPreviewDefault() {
6 | return (
7 |
8 | {
15 | await new Promise((resolve) => setTimeout(resolve, 2000));
16 | console.log("Sent email:", data);
17 | alert("Email sent! Check console for data.");
18 | }}
19 | />
20 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/components/ui/separator.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as SeparatorPrimitive from "@radix-ui/react-separator";
4 | import type * as React from "react";
5 |
6 | import { cn } from "@/lib/utils";
7 |
8 | function Separator({
9 | className,
10 | orientation = "horizontal",
11 | decorative = true,
12 | ...props
13 | }: React.ComponentProps) {
14 | return (
15 |
25 | );
26 | }
27 |
28 | export { Separator };
29 |
--------------------------------------------------------------------------------
/components/previews/code-block/line-numbers.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { CodeBlock } from "@/registry/new-york/ui/code-block";
4 |
5 | export default function CodeBlockLineNumbers() {
6 | const code = `import React from "react";
7 | import { cn } from "@/lib/utils";
8 |
9 | export function Component({ className }) {
10 | const [count, setCount] = React.useState(0);
11 |
12 | return (
13 |
14 |
Count: {count}
15 | setCount(count + 1)}>
16 | Increment
17 |
18 |
19 | );
20 | }`;
21 |
22 | return (
23 |
24 |
25 | {code}
26 |
27 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/components/previews/twitter-card/default.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { TwitterCard } from "@/registry/new-york/ui/twitter-card";
4 |
5 | // Static date for demo purposes - avoids React purity rules
6 | const DEMO_TIMESTAMP = new Date("2025-12-15T08:55:00Z");
7 |
8 | export default function TwitterCardDefault() {
9 | return (
10 |
11 |
24 |
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/components/previews/file-preview/uploading.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { FilePreview } from "@/registry/new-york/ui/file-preview";
4 |
5 | export default function FilePreviewUploading() {
6 | const files = [
7 | {
8 | id: "1",
9 | url: "#",
10 | name: "large-video.mp4",
11 | type: "video/mp4",
12 | isUploading: true,
13 | },
14 | {
15 | id: "2",
16 | url: "#",
17 | name: "completed.pdf",
18 | type: "application/pdf",
19 | isUploading: false,
20 | },
21 | {
22 | id: "3",
23 | url: "#",
24 | name: "audio-file.mp3",
25 | type: "audio/mpeg",
26 | isUploading: true,
27 | },
28 | ];
29 |
30 | return (
31 |
32 | console.log("Remove file:", id)}
35 | />
36 |
37 | );
38 | }
39 |
--------------------------------------------------------------------------------
/components/previews/author-tooltip/sizes.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { AuthorTooltip } from "@/registry/new-york/ui/author-tooltip";
4 |
5 | export default function AuthorTooltipSizes() {
6 | return (
7 | <>
8 |
16 |
24 |
32 | >
33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/content/docs/roadmap.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Roadmap"
3 | description: "Planned features and component development progress for GAIA UI"
4 | ---
5 |
6 | # Roadmap
7 |
8 | - [x] Composer with SlashCommandDropdown
9 | - [x] CLI command tab for different package managers
10 | - [x] Code Block
11 | - [x] Model Selector
12 | - [x] File Upload (as File Preview)
13 | - [x] Twitter Card
14 | - [ ] Email Preview
15 | - [x] Email Compose Card
16 | - [x] Calendar Event Card
17 | - [x] Todo task item
18 | - [x] Goal Graph (as Goal Card)
19 | - [x] File DropZone
20 | - [x] Workflow Card with Rotated Icons
21 | - [x] Notification Stack Card (as Notification Card)
22 | - [ ] Multi Step Navigation in Settings Menu
23 | - [x] Component Preview Tooltip (for sidebar hover previews)
24 | - [ ] Profile Holo Card
25 |
26 | ---
27 |
28 | **Have an idea?** [Submit a feature request](https://terrific-address-fab.notion.site/2c0023640e7b80c8b0f0ffba5046e852?pvs=105)
29 |
--------------------------------------------------------------------------------
/components/ui/kbd.tsx:
--------------------------------------------------------------------------------
1 | import { cn } from "@/lib/utils";
2 |
3 | function Kbd({ className, ...props }: React.ComponentProps<"kbd">) {
4 | return (
5 |
15 | );
16 | }
17 |
18 | function KbdGroup({ className, ...props }: React.ComponentProps<"div">) {
19 | return (
20 |
25 | );
26 | }
27 |
28 | export { Kbd, KbdGroup };
29 |
--------------------------------------------------------------------------------
/components/previews/todo-item/priorities.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { TodoItem } from "@/registry/new-york/ui/TodoItem";
4 |
5 | export default function TodoItemPriorities() {
6 | const todos = [
7 | {
8 | id: "1",
9 | title: "Fix critical bug in production",
10 | priority: "high" as const,
11 | completed: false,
12 | },
13 | {
14 | id: "2",
15 | title: "Update documentation",
16 | priority: "medium" as const,
17 | completed: false,
18 | },
19 | {
20 | id: "3",
21 | title: "Refactor legacy code",
22 | priority: "low" as const,
23 | completed: false,
24 | },
25 | {
26 | id: "4",
27 | title: "Review team submissions",
28 | priority: "none" as const,
29 | completed: false,
30 | },
31 | ];
32 |
33 | return (
34 |
35 | {todos.map((todo) => (
36 |
37 | ))}
38 |
39 | );
40 | }
41 |
--------------------------------------------------------------------------------
/components/previews/component-preview-tooltip/default.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { ComponentPreviewTooltip } from "@/registry/new-york/ui/component-preview-tooltip";
4 |
5 | export default function ComponentPreviewTooltipDefault() {
6 | return (
7 |
8 |
9 |
13 | Hover me: Todo Item
14 |
15 |
16 |
17 |
18 |
22 | Hover me: Notification Card
23 |
24 |
25 |
26 | );
27 | }
28 |
--------------------------------------------------------------------------------
/components/ui/theme-toggle.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useTheme } from "next-themes";
4 | import * as React from "react";
5 | import { HugeiconsIcon, Moon02Icon, Sun03Icon } from "@/components/icons";
6 | import { Toggle } from "@/components/ui/toggle";
7 |
8 | export function ThemeToggle() {
9 | const { theme, setTheme } = useTheme();
10 | const [mounted, setMounted] = React.useState(false);
11 |
12 | React.useEffect(() => {
13 | setMounted(true);
14 | }, []);
15 |
16 | if (!mounted) {
17 | return null;
18 | }
19 |
20 | const isDark = theme === "dark";
21 |
22 | return (
23 | setTheme(pressed ? "dark" : "light")}
28 | >
29 | {isDark ? (
30 |
31 | ) : (
32 |
33 | )}
34 |
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/components/previews/todo-item/default.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { TodoItem } from "@/registry/new-york/ui/TodoItem";
4 |
5 | // Static date for demo purposes - avoids React purity rules
6 | const DEMO_TOMORROW = new Date("2025-12-16T00:00:00Z");
7 |
8 | export default function TodoItemDefault() {
9 | return (
10 |
11 |
28 |
29 | );
30 | }
31 |
--------------------------------------------------------------------------------
/components/previews/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Preview Components Registry
3 | * Auto-registers all preview components using dynamic imports
4 | */
5 |
6 | import type { ComponentType } from "react";
7 |
8 | type PreviewComponent = ComponentType;
9 |
10 | /**
11 | * Get a preview component by its name
12 | * Handles nested folder structure with path format: component/variant
13 | * Examples: "navbar-menu/basic" -> previews/navbar-menu/basic.tsx
14 | * "raised-button/default" -> previews/raised-button/default.tsx
15 | */
16 | export async function getPreviewComponent(
17 | name: string,
18 | ): Promise {
19 | try {
20 | // Dynamic import based on path (folder/file)
21 | // Next.js will bundle all files in the previews directory
22 | const component_module = await import(`@/components/previews/${name}`);
23 | return component_module.default;
24 | } catch (error) {
25 | console.error(`Error loading preview component: ${name}`, error);
26 | return null;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/components/ui/input.tsx:
--------------------------------------------------------------------------------
1 | import type * as React from "react";
2 |
3 | import { cn } from "@/lib/utils";
4 |
5 | function Input({ className, type, ...props }: React.ComponentProps<"input">) {
6 | return (
7 |
18 | );
19 | }
20 |
21 | export { Input };
22 |
--------------------------------------------------------------------------------
/components/previews/calendar-event-card/default.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import {
4 | CalendarEventCard,
5 | EventLocation,
6 | EventTime,
7 | EventTitle,
8 | } from "@/registry/new-york/ui/calendar-event-card";
9 |
10 | export default function CalendarEventCardDefault() {
11 | return (
12 |
13 |
14 | Team Standup
15 |
16 | Google Meet
17 |
18 |
19 |
20 | Product Review
21 |
22 | Conference Room A
23 |
24 |
25 |
26 | Client Call
27 |
28 | Zoom
29 |
30 |
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/components/previews/weather-card/default.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { WeatherCard } from "@/registry/new-york/ui/weather-card";
4 |
5 | const sampleWeatherData = {
6 | coord: {
7 | lon: -122.4194,
8 | lat: 37.7749,
9 | },
10 | weather: [
11 | {
12 | id: 800,
13 | main: "Clear",
14 | description: "clear sky",
15 | icon: "01d",
16 | },
17 | ],
18 | main: {
19 | temp: 22,
20 | feels_like: 21,
21 | temp_min: 18,
22 | temp_max: 25,
23 | pressure: 1013,
24 | humidity: 60,
25 | },
26 | visibility: 10000,
27 | wind: {
28 | speed: 3.5,
29 | deg: 180,
30 | },
31 | dt: 1699641600,
32 | sys: {
33 | country: "US",
34 | sunrise: 1699622400,
35 | sunset: 1699660800,
36 | },
37 | timezone: -28800,
38 | name: "San Francisco",
39 | location: {
40 | city: "San Francisco",
41 | country: "United States",
42 | region: "California",
43 | },
44 | };
45 |
46 | export default function WeatherCardDefault() {
47 | return (
48 |
49 |
50 |
51 | );
52 | }
53 |
--------------------------------------------------------------------------------
/components/previews/holo-card/default.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { HoloCard } from "@/registry/new-york/ui/holo-card";
4 |
5 | const sampleProfileData = {
6 | name: "Elena Starweaver",
7 | subtitle: "The Curious Explorer",
8 | description:
9 | "Passionate about building beautiful user interfaces and exploring the intersection of design and technology. Always learning, always creating.",
10 | primaryId: "00042",
11 | secondaryInfo: "December 2024",
12 | badge: "Bluehaven",
13 | backgroundImage:
14 | "https://i.pinimg.com/1200x/27/0a/74/270a74bdc412f9eeae4d2403ebc9bd63.jpg",
15 | overlayColor: "#4f46e5",
16 | overlayOpacity: 30,
17 | };
18 |
19 | const sampleBranding = {
20 | logo: "/media/text_w_logo_white.webp",
21 | logoAlt: "GAIA Logo",
22 | icon: "/media/experience logo.svg",
23 | iconAlt: "Experience Icon",
24 | };
25 |
26 | export default function HoloCardDefault() {
27 | return (
28 |
29 |
34 |
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/components/previews/search-results-tabs/with-image-click.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useState } from "react";
4 |
5 | import { SearchResultsTabs } from "@/registry/new-york/ui/search-results-tabs";
6 |
7 | export default function SearchResultsTabsWithImageClick() {
8 | const [clickedImage, setClickedImage] = useState(null);
9 |
10 | const searchResults = {
11 | images: [
12 | "https://images.unsplash.com/photo-1633356122544-f134324a6cee?w=800",
13 | "https://images.unsplash.com/photo-1677442136019-21780ecad995?w=800",
14 | "https://images.unsplash.com/photo-1620712943543-bcc4688e7485?w=800",
15 | ],
16 | };
17 |
18 | return (
19 |
20 |
24 | {clickedImage && (
25 |
26 |
27 | Clicked: {clickedImage}
28 |
29 |
30 | )}
31 |
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/components/previews/file-preview/default.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { FilePreview } from "@/registry/new-york/ui/file-preview";
4 |
5 | export default function FilePreviewDefault() {
6 | const files = [
7 | {
8 | id: "1",
9 | url: "https://images.unsplash.com/photo-1560807707-8cc77767d783?w=100&h=100&fit=crop",
10 | name: "photo.jpg",
11 | type: "image/jpeg",
12 | },
13 | {
14 | id: "2",
15 | url: "#",
16 | name: "document.pdf",
17 | type: "application/pdf",
18 | },
19 | {
20 | id: "3",
21 | url: "#",
22 | name: "spreadsheet.xlsx",
23 | type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
24 | },
25 | {
26 | id: "4",
27 | url: "#",
28 | name: "script.tsx",
29 | type: "text/typescript",
30 | },
31 | {
32 | id: "5",
33 | url: "#",
34 | name: "archive.zip",
35 | type: "application/zip",
36 | },
37 | ];
38 |
39 | return (
40 |
41 | console.log("Remove file:", id)}
44 | />
45 |
46 | );
47 | }
48 |
--------------------------------------------------------------------------------
/app/robots.ts:
--------------------------------------------------------------------------------
1 | import type { MetadataRoute } from "next";
2 | import { siteConfig } from "@/lib/siteConfig";
3 |
4 | /**
5 | * Generate robots.txt for search engine crawlers
6 | * Configures crawl rules, sitemaps, and special directives for major search engines
7 | */
8 | export default function robots(): MetadataRoute.Robots {
9 | return {
10 | rules: [
11 | {
12 | userAgent: "*",
13 | allow: "/",
14 | disallow: ["/api/", "/private/", "/_next/"],
15 | crawlDelay: 0,
16 | },
17 | {
18 | userAgent: "Googlebot",
19 | allow: "/",
20 | disallow: ["/api/", "/private/"],
21 | },
22 | {
23 | userAgent: "Googlebot-Image",
24 | allow: "/",
25 | },
26 | {
27 | userAgent: "Bingbot",
28 | allow: "/",
29 | disallow: ["/api/", "/private/"],
30 | },
31 | {
32 | userAgent: "Slurp", // Yahoo
33 | allow: "/",
34 | disallow: ["/api/", "/private/"],
35 | },
36 | {
37 | userAgent: "DuckDuckBot",
38 | allow: "/",
39 | disallow: ["/api/", "/private/"],
40 | },
41 | ],
42 | sitemap: `${siteConfig.url}/sitemap.xml`,
43 | host: siteConfig.url,
44 | };
45 | }
46 |
--------------------------------------------------------------------------------
/components/previews/code-block/languages.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { CodeBlock } from "@/registry/new-york/ui/code-block";
4 |
5 | export default function CodeBlockLanguages() {
6 | const pythonCode = `def fibonacci(n):
7 | if n <= 1:
8 | return n
9 | return fibonacci(n-1) + fibonacci(n-2)
10 |
11 | print(fibonacci(10))`;
12 |
13 | const typescriptCode = `interface User {
14 | id: string;
15 | name: string;
16 | email: string;
17 | }
18 |
19 | const createUser = (data: User): User => {
20 | return { ...data };
21 | };`;
22 |
23 | const bashCode = `#!/bin/bash
24 | # Deploy script
25 | echo "Building application..."
26 | npm run build
27 | echo "Deploying to production..."
28 | npm run deploy`;
29 |
30 | return (
31 |
32 |
33 | {pythonCode}
34 |
35 |
36 |
37 | {typescriptCode}
38 |
39 |
40 |
41 | {bashCode}
42 |
43 |
44 | );
45 | }
46 |
--------------------------------------------------------------------------------
/components/previews/message-bubble/grouped.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { MessageBubble } from "@/registry/new-york/ui/message-bubble";
4 |
5 | export default function MessageBubbleGrouped() {
6 | return (
7 |
8 |
13 |
18 |
23 |
28 |
29 |
34 |
39 |
40 | );
41 | }
42 |
--------------------------------------------------------------------------------
/components/icons/github.tsx:
--------------------------------------------------------------------------------
1 | import type { SVGProps } from "react";
2 |
3 | const GitHub = (props: SVGProps) => (
4 |
5 | GitHub
6 |
13 |
14 | );
15 |
16 | export { GitHub };
17 |
--------------------------------------------------------------------------------
/components/previews/link-preview/default.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { LinkPreview } from "@/registry/new-york/ui/link-preview";
4 |
5 | export default function LinkPreviewDefault() {
6 | return (
7 |
8 |
9 | GAIA is an open-source AI assistant designed to help you manage your
10 | digital life. Built with modern web technologies, it provides a seamless
11 | experience for task automation, email management, and calendar
12 | integration. Visit{" "}
13 | GAIA to learn more
14 | about the platform, or check out{" "}
15 |
16 | The Experience Company
17 | {" "}
18 | to see who's behind the project. The platform is actively
19 | maintained and welcomes contributions from developers around the world.
20 |
21 |
22 | Check out{" "}
23 |
24 | GAIA on GitHub
25 | {" "}
26 |
27 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/components/previews/composer/with-files.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useState } from "react";
4 | import { Composer, type UploadedFile } from "@/registry/new-york/ui/composer";
5 |
6 | export default function ComposerWithFiles() {
7 | const [files, setFiles] = useState([
8 | {
9 | id: "1",
10 | name: "document.pdf",
11 | type: "application/pdf",
12 | url: "",
13 | },
14 | {
15 | id: "2",
16 | name: "screenshot.png",
17 | type: "image/png",
18 | url: "https://github.com/aryanranderiya.png",
19 | },
20 | {
21 | id: "3",
22 | name: "data.json",
23 | type: "application/json",
24 | url: "",
25 | },
26 | ]);
27 |
28 | const handleRemoveFile = (id: string) => {
29 | setFiles((prev) => prev.filter((f) => f.id !== id));
30 | };
31 |
32 | return (
33 |
34 | {
39 | console.log("Message:", message);
40 | console.log("Files:", attachedFiles);
41 | }}
42 | />
43 |
44 | );
45 | }
46 |
--------------------------------------------------------------------------------
/components/core/copy-button.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as React from "react";
4 | import { Copy01Icon, HugeiconsIcon, Tick02Icon } from "@/components/icons";
5 | import { Button } from "@/components/ui/button";
6 | import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip";
7 |
8 | interface CopyButtonProps {
9 | value: string;
10 | }
11 |
12 | export function CopyButton({ value }: CopyButtonProps) {
13 | const [copied, setCopied] = React.useState(false);
14 |
15 | const copy = async () => {
16 | await navigator.clipboard.writeText(value);
17 | setCopied(true);
18 | setTimeout(() => setCopied(false), 2000);
19 | };
20 |
21 | return (
22 |
23 |
24 |
30 | {copied ? (
31 |
32 | ) : (
33 |
34 | )}
35 |
36 |
37 | Copy Code to Clipboard
38 |
39 | );
40 | }
41 |
--------------------------------------------------------------------------------
/lib/siteConfig.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Site configuration - comprehensive site metadata
3 | */
4 | export const siteConfig = {
5 | name: "GAIA UI",
6 | url: "https://ui.heygaia.io",
7 | ogImage: "https://ui.heygaia.io/og-image.png",
8 | description:
9 | "Beautiful, accessible React components from the GAIA AI assistant project. Free and open source UI library built with Radix UI and Tailwind CSS for building modern chatbots and AI interfaces.",
10 | tagline: "Open Source Components for AI Assistants",
11 | links: {
12 | github: "https://github.com/heygaia/ui",
13 | experienceCompany: "https://experience.heygaia.io",
14 | twitter: "https://twitter.com/trygaia",
15 | discord: "https://discord.heygaia.io",
16 | "feature-request":
17 | "https://terrific-address-fab.notion.site/2c0023640e7b80c8b0f0ffba5046e852?pvs=105",
18 | },
19 | keywords: [
20 | "GAIA UI",
21 | "React Components",
22 | "UI Library",
23 | "Component Library",
24 | "AI Assistant UI",
25 | "Chatbot Components",
26 | "Radix UI",
27 | "Tailwind CSS",
28 | "TypeScript",
29 | "Accessible Components",
30 | "Open Source",
31 | "Free Components",
32 | "Modern UI",
33 | "Design System",
34 | ],
35 | };
36 |
--------------------------------------------------------------------------------
/components/core/code-block.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import type * as React from "react";
4 | import { CopyButton } from "@/components/core/copy-button";
5 | import { cn } from "@/lib/utils";
6 |
7 | interface CodeBlockProps extends React.HTMLAttributes {
8 | children: React.ReactNode;
9 | raw?: string;
10 | }
11 |
12 | export function CodeBlock({
13 | className,
14 | children,
15 | raw,
16 | ...props
17 | }: CodeBlockProps) {
18 | const textContent =
19 | raw ||
20 | (typeof children === "object" &&
21 | children &&
22 | "props" in children &&
23 | typeof children.props === "object" &&
24 | children.props &&
25 | "children" in children.props
26 | ? String(children.props.children)
27 | : "");
28 |
29 | return (
30 |
31 |
38 | {children}
39 |
40 |
41 |
42 |
43 |
44 | );
45 | }
46 |
--------------------------------------------------------------------------------
/content/docs/components.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Components"
3 | description: "Here you can find all the components available in the library. We are working on adding more components."
4 | ---
5 |
6 | import { ComponentPreviewTooltip } from "@/registry/new-york/ui/component-preview-tooltip";
7 | import { getNavigation } from "@/lib/navigation";
8 | import Link from "next/link";
9 |
10 | export const ComponentsList = () => {
11 | const navigation = getNavigation();
12 | const componentsSection = navigation.find(
13 | (section) => section.title === "Components"
14 | );
15 | const components = componentsSection?.items || [];
16 |
17 | return (
18 |
19 |
20 | {components.map((component, index) => {
21 | const componentName = component.href.split("/").pop();
22 | return (
23 |
24 |
25 |
26 | {component.title}
27 |
28 |
29 |
30 | );
31 | })}
32 |
33 | ); };
34 |
35 |
36 |
--------------------------------------------------------------------------------
/components/previews/search-results-tabs/web-only.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { SearchResultsTabs } from "@/registry/new-york/ui/search-results-tabs";
4 |
5 | export default function SearchResultsTabsWebOnly() {
6 | const searchResults = {
7 | web: [
8 | {
9 | title: "TypeScript: JavaScript With Syntax For Types",
10 | url: "https://www.typescriptlang.org",
11 | content:
12 | "TypeScript is a strongly typed programming language that builds on JavaScript, giving you better tooling at any scale.",
13 | },
14 | {
15 | title: "Tailwind CSS - Rapidly build modern websites",
16 | url: "https://tailwindcss.com",
17 | content:
18 | "A utility-first CSS framework packed with classes that can be composed to build any design, directly in your markup.",
19 | },
20 | {
21 | title: "Vercel: Build and deploy the best web experiences",
22 | url: "https://vercel.com",
23 | content:
24 | "Vercel is the platform for frontend developers, providing the speed and reliability innovators need to create at the moment of inspiration.",
25 | },
26 | ],
27 | };
28 |
29 | return (
30 |
31 |
32 |
33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/app/manifest.ts:
--------------------------------------------------------------------------------
1 | import type { MetadataRoute } from "next";
2 | import { siteConfig } from "@/lib/siteConfig";
3 |
4 | /**
5 | * Generate Web App Manifest for PWA support and better mobile experience
6 | * Includes shortcuts, categories, and proper theme handling
7 | */
8 | export default function manifest(): MetadataRoute.Manifest {
9 | return {
10 | name: siteConfig.name,
11 | short_name: "GAIA UI",
12 | description: siteConfig.description,
13 | start_url: "/",
14 | scope: "/",
15 | display: "standalone",
16 | background_color: "#ffffff",
17 | theme_color: "#000000",
18 | orientation: "portrait-primary",
19 | categories: ["development", "productivity", "utilities", "design"],
20 | lang: "en",
21 | dir: "ltr",
22 | icons: [
23 | {
24 | src: "/android-chrome-192x192.png",
25 | sizes: "192x192",
26 | type: "image/png",
27 | purpose: "any",
28 | },
29 | {
30 | src: "/android-chrome-512x512.png",
31 | sizes: "512x512",
32 | type: "image/png",
33 | purpose: "any",
34 | },
35 | {
36 | src: "/favicon-16x16.png",
37 | sizes: "16x16",
38 | type: "image/png",
39 | },
40 | {
41 | src: "/favicon-32x32.png",
42 | sizes: "32x32",
43 | type: "image/png",
44 | },
45 | ],
46 | };
47 | }
48 |
--------------------------------------------------------------------------------
/components/core/component-preview.tsx:
--------------------------------------------------------------------------------
1 | import type * as React from "react";
2 | import { getPreviewComponent } from "@/components/previews";
3 | import { getSourceCode } from "@/lib/source";
4 | import { ComponentPreviewClient } from "./component-preview-client";
5 |
6 | interface ComponentPreviewProps extends React.HTMLAttributes {
7 | name?: string;
8 | code?: string;
9 | children?: React.ReactNode;
10 | }
11 |
12 | /**
13 | * Server component for component preview
14 | * Reads source code and dynamically loads preview component
15 | */
16 | export async function ComponentPreview({
17 | name,
18 | code,
19 | children,
20 | ...props
21 | }: ComponentPreviewProps) {
22 | let sourceCode = code || "";
23 | let PreviewComponent = null;
24 |
25 | // If name is provided, read code and dynamically load component
26 | // Name format: component/variant (e.g., "navbar-menu/basic")
27 | if (name) {
28 | const previewPath = `components/previews/${name}.tsx`;
29 | sourceCode = getSourceCode(previewPath);
30 | PreviewComponent = await getPreviewComponent(name);
31 | }
32 |
33 | return (
34 |
35 | {PreviewComponent ? : children}
36 |
37 | );
38 | }
39 |
--------------------------------------------------------------------------------
/components/ui/avatar.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as AvatarPrimitive from "@radix-ui/react-avatar";
4 | import type * as React from "react";
5 |
6 | import { cn } from "@/lib/utils";
7 |
8 | function Avatar({
9 | className,
10 | ...props
11 | }: React.ComponentProps) {
12 | return (
13 |
21 | );
22 | }
23 |
24 | function AvatarImage({
25 | className,
26 | ...props
27 | }: React.ComponentProps) {
28 | return (
29 |
34 | );
35 | }
36 |
37 | function AvatarFallback({
38 | className,
39 | ...props
40 | }: React.ComponentProps) {
41 | return (
42 |
50 | );
51 | }
52 |
53 | export { Avatar, AvatarImage, AvatarFallback };
54 |
--------------------------------------------------------------------------------
/components/core/fullscreen-button.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import type * as React from "react";
4 | import { FullScreenIcon, HugeiconsIcon } from "@/components/icons";
5 | import { Button } from "@/components/ui/button";
6 | import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog";
7 | import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip";
8 |
9 | interface FullscreenButtonProps {
10 | children: React.ReactNode;
11 | }
12 |
13 | export function FullscreenButton({ children }: FullscreenButtonProps) {
14 | return (
15 |
16 |
17 |
18 |
19 |
24 |
25 |
26 |
27 |
28 | Open in Full Screen
29 |
30 |
31 |
32 | {children}
33 |
34 |
35 |
36 | );
37 | }
38 |
--------------------------------------------------------------------------------
/components/previews/weather-card/units.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { WeatherCard } from "@/registry/new-york/ui/weather-card";
4 |
5 | const sampleWeatherData = {
6 | coord: { lon: 2.3522, lat: 48.8566 },
7 | weather: [
8 | {
9 | id: 800,
10 | main: "Clear",
11 | description: "clear sky",
12 | icon: "01d",
13 | },
14 | ],
15 | main: {
16 | temp: 20,
17 | feels_like: 19,
18 | temp_min: 18,
19 | temp_max: 22,
20 | pressure: 1013,
21 | humidity: 65,
22 | },
23 | visibility: 10000,
24 | wind: {
25 | speed: 3.0,
26 | deg: 180,
27 | },
28 | dt: 1699641600,
29 | sys: {
30 | country: "FR",
31 | sunrise: 1699600800,
32 | sunset: 1699636800,
33 | },
34 | timezone: 3600,
35 | name: "Paris",
36 | location: {
37 | city: "Paris",
38 | country: "France",
39 | region: "Île-de-France",
40 | },
41 | };
42 |
43 | export default function WeatherCardUnits() {
44 | return (
45 |
46 |
52 |
58 |
59 | );
60 | }
61 |
--------------------------------------------------------------------------------
/components/previews/message-bubble/default.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { ChatMessage } from "@/registry/new-york/ui/message-bubble";
4 |
5 | export default function MessageBubbleDefault() {
6 | const messages = [
7 | {
8 | id: "1",
9 | variant: "received" as const,
10 | messages: ["Hey! How are you doing?"],
11 | timestamp: "10:30 AM",
12 | },
13 | {
14 | id: "2",
15 | variant: "sent" as const,
16 | messages: ["I'm doing great! Just working on some new features."],
17 | timestamp: "10:31 AM",
18 | },
19 | {
20 | id: "3",
21 | variant: "received" as const,
22 | messages: [
23 | "That sounds exciting!",
24 | "What are you building?",
25 | "I'd love to hear more about it.",
26 | ],
27 | timestamp: "10:32 AM",
28 | },
29 | {
30 | id: "4",
31 | variant: "sent" as const,
32 | messages: [
33 | "I'm building a new UI component library!",
34 | "It has beautiful iOS-style message bubbles just like these 😊",
35 | ],
36 | timestamp: "10:33 AM",
37 | },
38 | ];
39 |
40 | return (
41 |
42 | {messages.map((message) => (
43 |
49 | ))}
50 |
51 | );
52 | }
53 |
--------------------------------------------------------------------------------
/components/core/source-code.tsx:
--------------------------------------------------------------------------------
1 | import { getSourceCode } from "@/lib/source";
2 | import { SourceCodeClient } from "./source-code-client";
3 |
4 | interface SourceCodeProps {
5 | filePath: string;
6 | title?: string;
7 | language?: string;
8 | }
9 |
10 | /**
11 | * Generate a display title from the file path
12 | * Examples:
13 | * - "registry/new-york/ui/author-tooltip.tsx" -> "components/ui/author-tooltip.tsx"
14 | * - "lib/utils/colorUtils.ts" -> "lib/utils/colorUtils.ts" (unchanged)
15 | */
16 | function generateTitle(filePath: string): string {
17 | // Convert registry paths to components paths
18 | return filePath.replace(/^registry\/[^/]+\//, "components/");
19 | }
20 |
21 | /**
22 | * Server component to display source code from a file
23 | * Automatically reads the file and displays it with syntax highlighting
24 | * Title is auto-generated from filePath if not provided
25 | */
26 | export function SourceCode({ filePath, title, language }: SourceCodeProps) {
27 | const code = getSourceCode(filePath);
28 |
29 | // Infer language from file extension if not provided
30 | const lang = language || filePath.split(".").pop() || "tsx";
31 |
32 | // Auto-generate title from filePath if not provided
33 | const displayTitle = title || generateTitle(filePath);
34 |
35 | return ;
36 | }
37 |
--------------------------------------------------------------------------------
/content/docs/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Introduction
3 | description: Beautiful, accessible components built with Radix UI and Tailwind CSS.
4 | logo: /media/gaiaui_logo.png
5 | ---
6 |
7 | Welcome to GAIA UI - a collection of beautifully designed, accessible components that you can copy and paste into your apps.
8 |
9 | ## Philosophy
10 |
11 | GAIA UI is built on the following principles:
12 |
13 | - **Accessible** - Built with accessibility in mind, using Radix UI primitives.
14 | - **Customizable** - Styled with Tailwind CSS. Easily customize to match your brand.
15 | - **Copy and paste** - No complex installation. Just copy the code into your project.
16 | - **Modern** - Built with the latest React patterns and best practices.
17 |
18 | ## Features
19 |
20 | - Beautiful design with attention to detail
21 | - WCAG compliant components built with Radix UI
22 | - Built with TypeScript for type safety
23 | - Dark mode support out of the box
24 | - Mobile-first responsive design
25 | - Optimized for performance with minimal bundle size
26 |
27 | ## Tech Stack
28 |
29 | GAIA UI is built using:
30 |
31 | - [React](https://react.dev/) - UI library
32 | - [Tailwind CSS](https://tailwindcss.com/) - Styling
33 | - [Radix UI](https://www.radix-ui.com/) - Accessible component primitives
34 | - [TypeScript](https://www.typescriptlang.org/) - Type safety
35 | - [Next.js](https://nextjs.org/) - React framework
36 |
--------------------------------------------------------------------------------
/registry/new-york/ui/weather-detail-item.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import type React from "react";
4 | import type { ReactNode } from "react";
5 | import {
6 | Tooltip,
7 | TooltipContent,
8 | TooltipProvider,
9 | TooltipTrigger,
10 | } from "@/components/ui/tooltip";
11 |
12 | export interface WeatherDetailItemProps {
13 | icon: ReactNode;
14 | label: string;
15 | value: string;
16 | tooltipText?: string;
17 | highlight: string;
18 | }
19 |
20 | export const WeatherDetailItem: React.FC = ({
21 | icon,
22 | label,
23 | value,
24 | tooltipText,
25 | highlight,
26 | }) => {
27 | return (
28 |
29 |
30 |
31 |
32 |
33 |
34 |
{label}
35 |
{value}
36 |
37 |
{icon}
38 |
39 |
40 | {tooltipText && (
41 |
42 | {tooltipText}
43 |
44 | )}
45 |
46 |
47 |
48 | );
49 | };
50 |
--------------------------------------------------------------------------------
/components/previews/model-selector/default.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useState } from "react";
4 | import {
5 | type AIModel,
6 | ModelSelector,
7 | } from "@/registry/new-york/ui/model-selector";
8 |
9 | const models: AIModel[] = [
10 | {
11 | id: "gpt-4o",
12 | name: "GPT-4o",
13 | provider: "OpenAI",
14 | icon: "https://upload.wikimedia.org/wikipedia/commons/0/04/ChatGPT_logo.svg",
15 | isPro: true,
16 | description: "Most capable model for complex tasks",
17 | },
18 | {
19 | id: "claude-3.5",
20 | name: "Claude 3.5 Sonnet",
21 | provider: "Anthropic",
22 | icon: "https://www.anthropic.com/favicon.ico",
23 | isPro: true,
24 | description: "Advanced reasoning and analysis",
25 | },
26 | {
27 | id: "gemini-pro",
28 | name: "Gemini Pro",
29 | provider: "Google",
30 | icon: "https://www.gstatic.com/lamda/images/gemini_sparkle_v002_d4735304ff6292a690345.svg",
31 | description: "Fast and efficient for most tasks",
32 | },
33 | {
34 | id: "llama-3",
35 | name: "Llama 3",
36 | provider: "Meta",
37 | description: "Open source and customizable",
38 | },
39 | ];
40 |
41 | export default function ModelSelectorDefault() {
42 | const [selectedModel, setSelectedModel] = useState(models[0]);
43 |
44 | return (
45 |
46 |
51 |
52 | );
53 | }
54 |
--------------------------------------------------------------------------------
/components/previews/message-bubble/grouped-rounded.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { MessageBubble } from "@/registry/new-york/ui/message-bubble";
4 |
5 | export default function MessageBubbleGroupedRounded() {
6 | return (
7 |
8 |
21 |
22 |
27 |
32 |
37 |
42 |
43 |
44 |
49 |
54 |
55 | );
56 | }
57 |
--------------------------------------------------------------------------------
/registry/new-york/ui/todo-item.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | export type TodoPriority = "high" | "medium" | "low" | "none";
4 |
5 | export interface TodoLabel {
6 | id: string;
7 | name: string;
8 | color?: string;
9 | }
10 |
11 | export interface TodoSubtask {
12 | id: string;
13 | title: string;
14 | completed: boolean;
15 | }
16 |
17 | export interface TodoProject {
18 | id: string;
19 | name: string;
20 | color?: string;
21 | }
22 |
23 | export interface TodoItemProps {
24 | id: string;
25 | title: string;
26 | description?: string;
27 | completed: boolean;
28 | priority: TodoPriority;
29 | dueDate?: string | Date;
30 | labels?: TodoLabel[];
31 | subtasks?: TodoSubtask[];
32 | project?: TodoProject;
33 | onToggleComplete?: (id: string, completed: boolean) => void;
34 | onClick?: (id: string) => void;
35 | isSelected?: boolean;
36 | className?: string;
37 | }
38 |
39 | export const priorityConfig = {
40 | high: {
41 | textColor: "text-red-500 dark:text-red-400",
42 | bgColor: "bg-red-500/10 dark:bg-red-400/10",
43 | borderColor: "border-red-500",
44 | },
45 | medium: {
46 | textColor: "text-yellow-600 dark:text-yellow-400",
47 | bgColor: "bg-yellow-500/10 dark:bg-yellow-400/10",
48 | borderColor: "border-yellow-500",
49 | },
50 | low: {
51 | textColor: "text-blue-500 dark:text-blue-400",
52 | bgColor: "bg-blue-500/10 dark:bg-blue-400/10",
53 | borderColor: "border-blue-500",
54 | },
55 | none: {
56 | textColor: "text-zinc-500 dark:text-zinc-500",
57 | bgColor: "bg-zinc-500/10 dark:bg-zinc-500/10",
58 | borderColor: "border-zinc-400 dark:border-zinc-500",
59 | },
60 | } as const;
61 |
--------------------------------------------------------------------------------
/components/previews/navbar-menu/basic.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import {
4 | CreditCardIcon,
5 | HugeiconsIcon,
6 | Idea01Icon,
7 | MapsIcon,
8 | Message01Icon,
9 | } from "@/components/icons";
10 | import { NavbarWithMenu } from "@/registry/new-york/ui/navbar-menu";
11 |
12 | export default function NavbarMenuBasic() {
13 | const sections = [
14 | {
15 | id: "pages",
16 | gridLayout: "grid w-full grid-cols-2 gap-4",
17 | links: [
18 | {
19 | label: "Get Started",
20 | href: "/login",
21 | description: "Sign Up / Login",
22 | icon: (
23 |
28 | ),
29 | },
30 | {
31 | label: "Use Cases",
32 | href: "/use-cases",
33 | description: "Discover workflows",
34 | icon: (
35 |
36 | ),
37 | },
38 | {
39 | label: "Pricing",
40 | href: "/pricing",
41 | description: "Choose your plan",
42 | icon: (
43 |
48 | ),
49 | },
50 | {
51 | label: "Roadmap",
52 | href: "/roadmap",
53 | description: "What's coming next",
54 | icon: (
55 |
56 | ),
57 | },
58 | ],
59 | },
60 | ];
61 |
62 | return (
63 |
67 | );
68 | }
69 |
--------------------------------------------------------------------------------
/components/ui/toggle.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as TogglePrimitive from "@radix-ui/react-toggle";
4 | import { cva, type VariantProps } from "class-variance-authority";
5 | import type * as React from "react";
6 |
7 | import { cn } from "@/lib/utils";
8 |
9 | const toggleVariants = cva(
10 | "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none transition-[color,box-shadow] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive whitespace-nowrap cursor-pointer",
11 | {
12 | variants: {
13 | variant: {
14 | default: "bg-transparent",
15 | outline:
16 | "border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground",
17 | },
18 | size: {
19 | default: "h-9 px-2 min-w-9",
20 | sm: "h-8 px-1.5 min-w-8",
21 | lg: "h-10 px-2.5 min-w-10",
22 | },
23 | },
24 | defaultVariants: {
25 | variant: "default",
26 | size: "default",
27 | },
28 | },
29 | );
30 |
31 | function Toggle({
32 | className,
33 | variant,
34 | size,
35 | ...props
36 | }: React.ComponentProps &
37 | VariantProps) {
38 | return (
39 |
44 | );
45 | }
46 |
47 | export { Toggle, toggleVariants };
48 |
--------------------------------------------------------------------------------
/components/ui/badge.tsx:
--------------------------------------------------------------------------------
1 | import { Slot } from "@radix-ui/react-slot";
2 | import { cva, type VariantProps } from "class-variance-authority";
3 | import type * as React from "react";
4 |
5 | import { cn } from "@/lib/utils";
6 |
7 | const badgeVariants = cva(
8 | "inline-flex items-center justify-center rounded-full border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-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 transition-[color,box-shadow] overflow-hidden",
9 | {
10 | variants: {
11 | variant: {
12 | default:
13 | "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
14 | secondary:
15 | "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
16 | destructive:
17 | "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
18 | outline:
19 | "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
20 | },
21 | },
22 | defaultVariants: {
23 | variant: "default",
24 | },
25 | },
26 | );
27 |
28 | function Badge({
29 | className,
30 | variant,
31 | asChild = false,
32 | ...props
33 | }: React.ComponentProps<"span"> &
34 | VariantProps & { asChild?: boolean }) {
35 | const Comp = asChild ? Slot : "span";
36 |
37 | return (
38 |
43 | );
44 | }
45 |
46 | export { Badge, badgeVariants };
47 |
--------------------------------------------------------------------------------
/components/previews/calendar-event-card/actions.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useState } from "react";
4 | import {
5 | CalendarEventCard,
6 | type EventStatus,
7 | EventTime,
8 | EventTitle,
9 | } from "@/registry/new-york/ui/calendar-event-card";
10 |
11 | export default function CalendarEventCardActions() {
12 | const [status1, setStatus1] = useState("idle");
13 | const [status2] = useState("completed");
14 | const [status3, setStatus3] = useState("idle");
15 |
16 | const handleConfirm = (setStatus: (s: EventStatus) => void) => {
17 | setStatus("loading");
18 | setTimeout(() => setStatus("completed"), 1500);
19 | };
20 |
21 | return (
22 |
23 | handleConfirm(setStatus1)}
28 | label="New Event"
29 | isDotted
30 | >
31 | Schedule Meeting
32 |
33 |
34 |
35 | {}}
40 | completedLabel="Confirmed"
41 | >
42 | Already Confirmed Event
43 |
44 |
45 |
46 | handleConfirm(setStatus3)}
52 | >
53 | Cancel Appointment
54 |
55 |
56 |
57 | );
58 | }
59 |
--------------------------------------------------------------------------------
/components/ui/popover.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as PopoverPrimitive from "@radix-ui/react-popover";
4 | import type * as React from "react";
5 |
6 | import { cn } from "@/lib/utils";
7 |
8 | function Popover({
9 | ...props
10 | }: React.ComponentProps) {
11 | return ;
12 | }
13 |
14 | function PopoverTrigger({
15 | ...props
16 | }: React.ComponentProps) {
17 | return ;
18 | }
19 |
20 | function PopoverContent({
21 | className,
22 | align = "center",
23 | sideOffset = 4,
24 | ...props
25 | }: React.ComponentProps) {
26 | return (
27 |
28 |
38 |
39 | );
40 | }
41 |
42 | function PopoverAnchor({
43 | ...props
44 | }: React.ComponentProps) {
45 | return ;
46 | }
47 |
48 | export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor };
49 |
--------------------------------------------------------------------------------
/components/ui/scroll-area.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
4 | import type * as React from "react";
5 |
6 | import { cn } from "@/lib/utils";
7 |
8 | function ScrollArea({
9 | className,
10 | children,
11 | ...props
12 | }: React.ComponentProps) {
13 | return (
14 |
19 |
23 | {children}
24 |
25 |
26 |
27 |
28 | );
29 | }
30 |
31 | function ScrollBar({
32 | className,
33 | orientation = "vertical",
34 | ...props
35 | }: React.ComponentProps) {
36 | return (
37 |
50 |
54 |
55 | );
56 | }
57 |
58 | export { ScrollArea, ScrollBar };
59 |
--------------------------------------------------------------------------------
/content/docs/components/raised-button.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Raised Button
3 | description: A beautiful 3D raised button with customizable colors and dynamic text contrast.
4 | ---
5 |
6 |
7 |
8 | ### Sizes
9 |
10 |
11 |
12 | ## Installation
13 |
14 |
15 |
16 | Automatic
17 | Manual
18 |
19 |
20 | {" "}
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | Copy and paste the following code into your project.
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | ## Usage
38 |
39 | ```tsx
40 | import { RaisedButton } from "@/components/ui/raised-button";
41 |
42 | export default function Example() {
43 | return Click me ;
44 | }
45 | ```
46 |
47 | ## Props
48 |
49 | | Prop | Type | Default | Description |
50 | | --------- | ----------------------------------- | --------- | ------------------------------------- |
51 | | size | "sm" \| "default" \| "lg" \| "icon" | "default" | Size of the button |
52 | | color | string | undefined | Custom color (hex, rgb, or CSS color) |
53 | | disabled | boolean | false | Whether the button is disabled |
54 | | className | string | undefined | Additional CSS classes |
55 |
--------------------------------------------------------------------------------
/next.config.ts:
--------------------------------------------------------------------------------
1 | import createMDX from "@next/mdx";
2 | import type { NextConfig } from "next";
3 |
4 | const useProIcons = process.env.USE_PRO_ICONS === "true";
5 | console.log(
6 | "🎨 USE_PRO_ICONS:",
7 | process.env.USE_PRO_ICONS,
8 | "→ Using",
9 | useProIcons ? "PRO" : "FREE",
10 | "icons",
11 | );
12 |
13 | const nextConfig: NextConfig = {
14 | pageExtensions: ["js", "jsx", "md", "mdx", "ts", "tsx"],
15 | images: {
16 | dangerouslyAllowSVG: true,
17 | remotePatterns: [
18 | {
19 | protocol: "https",
20 | hostname: "**",
21 | },
22 | {
23 | protocol: "http",
24 | hostname: "**",
25 | },
26 | ],
27 | },
28 | // Turbopack config for icon alias
29 | ...(useProIcons && {
30 | turbopack: {
31 | resolveAlias: {
32 | "@hugeicons/core-free-icons": "@hugeicons-pro/core-solid-rounded",
33 | },
34 | },
35 | }),
36 | // Webpack config for icon alias (fallback if not using Turbopack)
37 | webpack: (config) => {
38 | if (useProIcons) {
39 | config.resolve.alias["@hugeicons/core-free-icons"] =
40 | "@hugeicons-pro/core-solid-rounded";
41 | }
42 | return config;
43 | },
44 | };
45 |
46 | // MDX config with serializable plugin references (for Turbopack compatibility)
47 | const withMDX = createMDX({
48 | options: {
49 | remarkPlugins: ["remark-gfm", "remark-frontmatter"],
50 | rehypePlugins: [
51 | "rehype-slug",
52 | [
53 | "rehype-pretty-code",
54 | {
55 | theme: {
56 | dark: "github-dark",
57 | light: "github-light",
58 | },
59 | keepBackground: false,
60 | },
61 | ],
62 | [
63 | "rehype-autolink-headings",
64 | {
65 | properties: {
66 | className: ["anchor"],
67 | ariaLabel: "Link to section",
68 | },
69 | },
70 | ],
71 | ],
72 | },
73 | });
74 |
75 | export default withMDX(nextConfig);
76 |
--------------------------------------------------------------------------------
/public/media/experience logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/content/docs/components/file-dropzone.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: File Dropzone
3 | description: A drag-and-drop file upload zone with preview, validation, and file management.
4 | ---
5 |
6 |
7 |
8 | ## Features
9 |
10 | - **Drag & Drop** - Drop files directly into the zone
11 | - **Click to Browse** - Fallback file input
12 | - **Image Preview** - Thumbnails for image files
13 | - **File Validation** - Size and count limits
14 | - **Remove Files** - Delete individual files
15 |
16 | ## Installation
17 |
18 |
19 |
20 | Automatic
21 | Manual
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | Copy and paste the following code into your project.
31 |
32 |
33 |
34 |
35 |
36 |
37 | ## Usage
38 |
39 | ```tsx
40 | import { FileDropzone } from "@/components/ui/file-dropzone";
41 |
42 | export default function Example() {
43 | return (
44 | console.log(files)}
46 | accept="image/*,.pdf"
47 | maxSize={5 * 1024 * 1024}
48 | maxFiles={5}
49 | />
50 | );
51 | }
52 | ```
53 |
54 | ## Props
55 |
56 | | Prop | Type | Default | Description |
57 | | --- | --- | --- | --- |
58 | | onFilesDropped | (files) => void | - | Callback with dropped files |
59 | | accept | string | - | Accepted file types |
60 | | multiple | boolean | true | Allow multiple files |
61 | | maxSize | number | 10MB | Max file size in bytes |
62 | | maxFiles | number | 10 | Maximum number of files |
63 | | disabled | boolean | false | Disable the dropzone |
64 |
--------------------------------------------------------------------------------
/content/docs/components/twitter-card.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Twitter Card
3 | description: A Twitter/X-style post card with author info, engagement metrics, and media support.
4 | ---
5 |
6 |
7 |
8 | ## Features
9 |
10 | - **Author Info** - Avatar, name, handle, verified badge
11 | - **Media Support** - Embedded images
12 | - **Quoted Tweets** - Nested quote support
13 | - **Engagement** - Likes, retweets, replies with actions
14 |
15 | ## Installation
16 |
17 |
18 |
19 | Automatic
20 | Manual
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | Copy and paste the following code into your project.
30 |
31 |
32 |
33 |
34 |
35 |
36 | ## Usage
37 |
38 | ```tsx
39 | import { TwitterCard } from "@/components/ui/twitter-card";
40 |
41 | export default function Example() {
42 | return (
43 |
55 | );
56 | }
57 | ```
58 |
59 | ## Props
60 |
61 | | Prop | Type | Description |
62 | | --- | --- | --- |
63 | | author | Author | Author information |
64 | | content | string | Tweet content |
65 | | timestamp | Date \| string | Post time |
66 | | likes | number | Like count |
67 | | retweets | number | Retweet count |
68 | | replies | number | Reply count |
69 | | media | string | Media image URL |
70 | | quoted | QuotedTweet | Quoted tweet |
71 |
--------------------------------------------------------------------------------
/components/previews/goal-card/default.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { GoalCard } from "@/registry/new-york/ui/goal-card";
4 |
5 | // Static dates for demo purposes - avoids React purity rules
6 | const DEMO_DATES = {
7 | oneWeekAgo: new Date("2025-12-08T00:00:00Z"),
8 | twoWeeksAgo: new Date("2025-12-01T00:00:00Z"),
9 | today: new Date("2025-12-15T00:00:00Z"),
10 | };
11 |
12 | export default function GoalCardDefault() {
13 | const goals = [
14 | {
15 | id: "1",
16 | title: "Launch MVP by end of quarter",
17 | progress: 75,
18 | createdAt: DEMO_DATES.oneWeekAgo,
19 | roadmap: {
20 | title: "Launch MVP by end of quarter",
21 | nodes: [
22 | { id: "1", title: "Design", isComplete: true },
23 | { id: "2", title: "Development", isComplete: true },
24 | { id: "3", title: "Testing", isComplete: true },
25 | { id: "4", title: "Launch", isComplete: false },
26 | ],
27 | },
28 | },
29 | {
30 | id: "2",
31 | title: "Complete user onboarding flow",
32 | progress: 100,
33 | createdAt: DEMO_DATES.twoWeeksAgo,
34 | roadmap: {
35 | title: "Complete user onboarding flow",
36 | nodes: [
37 | { id: "1", title: "Research", isComplete: true },
38 | { id: "2", title: "Design", isComplete: true },
39 | { id: "3", title: "Implement", isComplete: true },
40 | ],
41 | },
42 | },
43 | {
44 | id: "3",
45 | title: "Fix critical performance issues",
46 | progress: 30,
47 | createdAt: DEMO_DATES.today,
48 | roadmap: {
49 | title: "Fix critical performance issues",
50 | nodes: [
51 | { id: "1", title: "Profile", isComplete: true },
52 | { id: "2", title: "Optimize", isComplete: false },
53 | { id: "3", title: "Test", isComplete: false },
54 | ],
55 | },
56 | },
57 | ];
58 |
59 | return (
60 |
61 | {goals.map((goal) => (
62 | console.log("View goal:", id)}
66 | />
67 | ))}
68 |
69 | );
70 | }
71 |
--------------------------------------------------------------------------------
/content/docs/components/goal-card.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Goal Card
3 | description: A card component for displaying goal progress with status indicators and step tracking.
4 | ---
5 |
6 |
7 |
8 | ## Features
9 |
10 | - **Progress Bar** - Visual progress indicator
11 | - **Status Badge** - Not started, in progress, completed, at risk
12 | - **Step Tracking** - Shows completed/total steps
13 | - **Due Date** - Displays deadline
14 |
15 | ## Installation
16 |
17 |
18 |
19 | Automatic
20 | Manual
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | Copy and paste the following code into your project.
30 |
31 |
32 |
33 |
34 |
35 |
36 | ## Usage
37 |
38 | ```tsx
39 | import { GoalCard } from "@/components/ui/goal-card";
40 |
41 | export default function Example() {
42 | return (
43 | console.log(id)}
53 | />
54 | );
55 | }
56 | ```
57 |
58 | ## Props
59 |
60 | | Prop | Type | Description |
61 | | --- | --- | --- |
62 | | id | string | Unique identifier |
63 | | title | string | Goal title |
64 | | description | string | Optional description |
65 | | progress | number | Progress 0-100 |
66 | | status | GoalStatus | Current status |
67 | | steps | GoalStep[] | Array of steps |
68 | | dueDate | Date \| string | Due date |
69 | | onClick | (id) => void | Click handler |
70 |
71 | ### GoalStatus
72 |
73 | `"not_started"` | `"in_progress"` | `"completed"` | `"at_risk"`
74 |
--------------------------------------------------------------------------------
/components/previews/search-results-tabs/news.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { MessageBubble } from "@/registry/new-york/ui/message-bubble";
4 | import { SearchResultsTabs } from "@/registry/new-york/ui/search-results-tabs";
5 |
6 | export default function SearchResultsTabsNews() {
7 | const searchResults = {
8 | web: [],
9 | images: [],
10 | news: [
11 | {
12 | title: "AI Assistants Transform Productivity in 2025",
13 | url: "https://example.com/news/ai-productivity",
14 | content:
15 | "New AI assistant platforms are revolutionizing how people manage their digital workflows, with open-source solutions leading the charge in innovation and user adoption.",
16 | score: 0.95,
17 | date: "2025-11-08",
18 | },
19 | {
20 | title: "Open Source Projects See Record Contributions",
21 | url: "https://example.com/news/open-source",
22 | content:
23 | "GitHub reports a 40% increase in contributions to open-source projects, with AI and productivity tools seeing the most growth across all categories.",
24 | score: 0.89,
25 | date: "2025-11-07",
26 | },
27 | {
28 | title: "The Future of Personal AI: What's Next?",
29 | url: "https://example.com/news/future-ai",
30 | content:
31 | "Industry experts predict that personal AI assistants will become as ubiquitous as smartphones, with new capabilities emerging every quarter.",
32 | score: 0.87,
33 | date: "2025-11-06",
34 | },
35 | ],
36 | };
37 |
38 | return (
39 |
40 |
41 |
42 | {/* iOS-style message bubbles */}
43 |
44 |
45 |
49 |
50 |
51 |
55 |
56 |
57 |
58 | );
59 | }
60 |
--------------------------------------------------------------------------------
/components/ui/tooltip.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as TooltipPrimitive from "@radix-ui/react-tooltip";
4 | import type * as React from "react";
5 |
6 | import { cn } from "@/lib/utils";
7 |
8 | function TooltipProvider({
9 | delayDuration = 0,
10 | ...props
11 | }: React.ComponentProps) {
12 | return (
13 |
18 | );
19 | }
20 |
21 | function Tooltip({
22 | ...props
23 | }: React.ComponentProps) {
24 | return (
25 |
26 |
27 |
28 | );
29 | }
30 |
31 | function TooltipTrigger({
32 | ...props
33 | }: React.ComponentProps) {
34 | return ;
35 | }
36 |
37 | function TooltipContent({
38 | className,
39 | sideOffset = 0,
40 | children,
41 | ...props
42 | }: React.ComponentProps) {
43 | return (
44 |
45 |
54 | {children}
55 |
56 |
57 |
58 | );
59 | }
60 |
61 | export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
62 |
--------------------------------------------------------------------------------
/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import "@fontsource/inter/400.css";
2 | import "@fontsource/inter/500.css";
3 | import "@fontsource/inter/600.css";
4 | import "@fontsource/inter/700.css";
5 | import type { Metadata } from "next";
6 | import { Instrument_Serif } from "next/font/google";
7 | import { ThemeProvider } from "next-themes";
8 | import "./globals.css";
9 |
10 | import { NavbarWrapper } from "@/components/core/navbar-wrapper";
11 | import {
12 | generateOrganizationSchema,
13 | generateSEO,
14 | generateWebsiteSchema,
15 | } from "@/lib/seo";
16 |
17 | const instrumentSerif = Instrument_Serif({
18 | weight: "400",
19 | subsets: ["latin"],
20 | variable: "--font-instrument-serif",
21 | display: "swap",
22 | });
23 |
24 | export const metadata: Metadata = {
25 | ...generateSEO(),
26 | metadataBase: new URL("https://ui.heygaia.io"),
27 | };
28 |
29 | export default function RootLayout({
30 | children,
31 | }: Readonly<{
32 | children: React.ReactNode;
33 | }>) {
34 | const organizationSchema = generateOrganizationSchema();
35 | const websiteSchema = generateWebsiteSchema();
36 |
37 | return (
38 |
43 |
44 |
49 | {/* JSON-LD Structured Data for better SEO */}
50 |
56 |
60 |
61 |
62 |
68 |
69 | {children}
70 |
71 |
72 |
73 | );
74 | }
75 |
--------------------------------------------------------------------------------
/content/docs/components/email-compose-card.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Email Compose Card
3 | description: An AI-powered email composition card with recipient validation, smart inputs, and beautiful styling.
4 | ---
5 |
6 |
7 |
8 | ## Overview
9 |
10 | The Email Compose Card is a comprehensive component for composing and sending emails, featuring:
11 | - Chip-based recipient input with validation
12 | - Subject and Body editing
13 | - Smart "Edit" and "View" modes
14 | - Integration ready for AI-generated content reviews
15 |
16 | ## Installation
17 |
18 |
19 |
20 | Automatic
21 | Manual
22 |
23 |
24 |
25 |
26 |
27 | 1. Copy and paste the following code into your project.
28 |
29 |
30 |
31 | 2. Update the import paths to match your project structure.
32 |
33 |
34 |
35 | ## Props
36 |
37 | ### EmailComposeCardProps
38 |
39 | | Prop | Type | Default | Description |
40 | | --- | --- | --- | --- |
41 | | subject | string | - | The subject line content |
42 | | body | string | - | The body content |
43 | | recipients | string[] | [] | Initial list of recipient emails |
44 | | mode | "view" \| "edit" | "edit" | Current display mode |
45 | | recipientQuery | string | - | Optional context string (e.g., prompt used to generate email) |
46 | | onSubjectChange | (value: string) => void | - | Callback when subject changes |
47 | | onBodyChange | (value: string) => void | - | Callback when body changes |
48 | | onRecipientsChange | (recipients: string[]) => void | - | Callback when recipients change |
49 | | onSend | () => void | - | Callback for send action |
50 | | onCancel | () => void | - | Callback for cancel action |
51 | | isSending | boolean | false | Loading state for send action |
52 |
--------------------------------------------------------------------------------
/components/previews/holo-card/colors.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { HoloCard } from "@/registry/new-york/ui/holo-card";
4 |
5 | // House background images from GAIA
6 | const HOUSE_IMAGES = {
7 | frostpeak:
8 | "https://i.pinimg.com/1200x/bf/1a/99/bf1a99c4c2cd8f378b9e4493f71e7e64.jpg",
9 | greenvale:
10 | "https://i.pinimg.com/1200x/3b/3e/11/3b3e1167fcfb0933070b6064ce9c72cd.jpg",
11 | bluehaven:
12 | "https://i.pinimg.com/1200x/27/0a/74/270a74bdc412f9eeae4d2403ebc9bd63.jpg",
13 | };
14 |
15 | const profiles = [
16 | {
17 | data: {
18 | name: "Aurora Frost",
19 | subtitle: "Keeper of the Northern Lights",
20 | primaryId: "00001",
21 | secondaryInfo: "January 2024",
22 | badge: "Frostpeak",
23 | backgroundImage: HOUSE_IMAGES.frostpeak,
24 | overlayColor: "#06b6d4", // Cyan
25 | overlayOpacity: 25,
26 | },
27 | },
28 | {
29 | data: {
30 | name: "Sage Willowmist",
31 | subtitle: "Guardian of the Ancient Grove",
32 | primaryId: "00023",
33 | secondaryInfo: "March 2024",
34 | badge: "Greenvale",
35 | backgroundImage: HOUSE_IMAGES.greenvale,
36 | overlayColor: "#22c55e", // Green
37 | overlayOpacity: 30,
38 | },
39 | },
40 | {
41 | data: {
42 | name: "Marina Deepwater",
43 | subtitle: "Voyager of the Azure Depths",
44 | primaryId: "00108",
45 | secondaryInfo: "June 2024",
46 | badge: "Bluehaven",
47 | backgroundImage: HOUSE_IMAGES.bluehaven,
48 | overlayColor: "#8b5cf6", // Violet
49 | overlayOpacity: 25,
50 | },
51 | },
52 | ];
53 |
54 | const sampleBranding = {
55 | logo: "/media/text_w_logo_white.webp",
56 | logoAlt: "GAIA Logo",
57 | icon: "/media/experience logo.svg",
58 | iconAlt: "Experience Icon",
59 | };
60 |
61 | export default function HoloCardColors() {
62 | return (
63 |
64 | {profiles.map((profile, index) => (
65 |
73 | ))}
74 |
75 | );
76 | }
77 |
--------------------------------------------------------------------------------
/content/docs/components/component-preview-tooltip.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Component Preview Tooltip
3 | description: A hover tooltip that shows a live preview of any component, perfect for navigation menus and component galleries.
4 | ---
5 |
6 |
7 |
8 | ## Why Use This?
9 |
10 | When browsing a component library, users often want to quickly see what a component looks like without navigating away from the current page. This tooltip provides instant visual feedback on hover, making it easier to find the right component.
11 |
12 | ## Features
13 |
14 | - **Dynamic Loading** - Lazy loads preview components on hover
15 | - **Positioning** - Show tooltip on left, right, top, or bottom
16 | - **Fallback** - Graceful handling when preview isn't available
17 | - **Animations** - Smooth fade and zoom transitions
18 |
19 | ## Installation
20 |
21 |
22 |
23 | Automatic
24 | Manual
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | Copy and paste the following code into your project.
34 |
35 |
36 |
37 |
38 |
39 |
40 | ## Usage
41 |
42 | ```tsx
43 | import { ComponentPreviewTooltip } from "@/components/ui/component-preview-tooltip";
44 |
45 | // In your sidebar or component list
46 |
47 |
48 | Todo Item
49 |
50 |
51 | ```
52 |
53 | ## Props
54 |
55 | | Prop | Type | Default | Description |
56 | | --- | --- | --- | --- |
57 | | componentName | string | - | Name of the component (matches preview folder name) |
58 | | children | ReactNode | - | Element that triggers the tooltip on hover |
59 | | side | "left" \| "right" \| "top" \| "bottom" | "right" | Tooltip position |
60 |
--------------------------------------------------------------------------------
/components/ui/tabs.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as TabsPrimitive from "@radix-ui/react-tabs";
4 | import type * as React from "react";
5 |
6 | import { cn } from "@/lib/utils";
7 |
8 | function Tabs({
9 | className,
10 | ...props
11 | }: React.ComponentProps) {
12 | return (
13 |
18 | );
19 | }
20 |
21 | function TabsList({
22 | className,
23 | ...props
24 | }: React.ComponentProps) {
25 | return (
26 |
34 | );
35 | }
36 |
37 | function TabsTrigger({
38 | className,
39 | ...props
40 | }: React.ComponentProps) {
41 | return (
42 |
50 | );
51 | }
52 |
53 | function TabsContent({
54 | className,
55 | ...props
56 | }: React.ComponentProps) {
57 | return (
58 |
63 | );
64 | }
65 |
66 | export { Tabs, TabsList, TabsTrigger, TabsContent };
67 |
--------------------------------------------------------------------------------
/components/ui/card.tsx:
--------------------------------------------------------------------------------
1 | import type * as React from "react";
2 |
3 | import { cn } from "@/lib/utils";
4 |
5 | function Card({ className, ...props }: React.ComponentProps<"div">) {
6 | return (
7 |
15 | );
16 | }
17 |
18 | function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
19 | return (
20 |
28 | );
29 | }
30 |
31 | function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
32 | return (
33 |
38 | );
39 | }
40 |
41 | function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
42 | return (
43 |
48 | );
49 | }
50 |
51 | function CardAction({ className, ...props }: React.ComponentProps<"div">) {
52 | return (
53 |
61 | );
62 | }
63 |
64 | function CardContent({ className, ...props }: React.ComponentProps<"div">) {
65 | return (
66 |
71 | );
72 | }
73 |
74 | function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
75 | return (
76 |
81 | );
82 | }
83 |
84 | export {
85 | Card,
86 | CardHeader,
87 | CardFooter,
88 | CardTitle,
89 | CardAction,
90 | CardDescription,
91 | CardContent,
92 | };
93 |
--------------------------------------------------------------------------------
/components/core/table-of-contents.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as React from "react";
4 | import { cn } from "@/lib/utils";
5 | import { Books02Icon, HugeiconsIcon } from "../icons";
6 |
7 | interface TocEntry {
8 | id: string;
9 | text: string;
10 | level: number;
11 | }
12 |
13 | interface TableOfContentsProps {
14 | toc: TocEntry[];
15 | }
16 |
17 | export function TableOfContents({ toc }: TableOfContentsProps) {
18 | const [activeId, setActiveId] = React.useState("");
19 |
20 | React.useEffect(() => {
21 | const observer = new IntersectionObserver(
22 | (entries) => {
23 | entries.forEach((entry) => {
24 | if (entry.isIntersecting) {
25 | setActiveId(entry.target.id);
26 | }
27 | });
28 | },
29 | { rootMargin: "-80px 0% -80% 0%" },
30 | );
31 |
32 | const headings = document.querySelectorAll("h2, h3, h4");
33 | headings.forEach((heading) => {
34 | observer.observe(heading);
35 | });
36 |
37 | return () => {
38 | headings.forEach((heading) => {
39 | observer.unobserve(heading);
40 | });
41 | };
42 | }, []);
43 |
44 | if (!toc || toc.length === 0) {
45 | return null;
46 | }
47 |
48 | return (
49 |
50 |
51 |
52 | On This Page
53 |
54 |
55 |
84 |
85 |
86 | );
87 | }
88 |
--------------------------------------------------------------------------------
/content/docs/components/model-selector.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Model Selector
3 | description: A dropdown selector for choosing AI models with provider information and pro badges.
4 | ---
5 |
6 |
7 |
8 | ## Features
9 |
10 | - **Model List** - Display available AI models with icons
11 | - **Provider Info** - Shows model provider (OpenAI, Anthropic, etc.)
12 | - **Pro Badge** - Indicates premium models
13 | - **Description** - Optional model description
14 |
15 | ## Installation
16 |
17 |
18 |
19 | Automatic
20 | Manual
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | Copy and paste the following code into your project.
30 |
31 |
32 |
33 |
34 |
35 |
36 | ## Usage
37 |
38 | ```tsx
39 | import { ModelSelector } from "@/components/ui/model-selector";
40 | import { useState } from "react";
41 |
42 | const models = [
43 | { id: "gpt-4", name: "GPT-4", provider: "OpenAI", isPro: true },
44 | { id: "claude", name: "Claude", provider: "Anthropic" },
45 | ];
46 |
47 | export default function Example() {
48 | const [selected, setSelected] = useState(models[0]);
49 |
50 | return (
51 |
56 | );
57 | }
58 | ```
59 |
60 | ## Props
61 |
62 | | Prop | Type | Description |
63 | | --- | --- | --- |
64 | | models | AIModel[] | Array of available models |
65 | | selectedModel | AIModel | Currently selected model |
66 | | onSelect | (model) => void | Selection callback |
67 | | disabled | boolean | Disable the selector |
68 |
69 | ### AIModel
70 |
71 | | Property | Type | Description |
72 | | --- | --- | --- |
73 | | id | string | Unique identifier |
74 | | name | string | Display name |
75 | | provider | string | Provider name |
76 | | icon | string | Icon URL |
77 | | isPro | boolean | Show pro badge |
78 | | description | string | Model description |
79 |
--------------------------------------------------------------------------------
/components/ui/button.tsx:
--------------------------------------------------------------------------------
1 | import { Slot } from "@radix-ui/react-slot";
2 | import { cva, type VariantProps } from "class-variance-authority";
3 | import type * as React from "react";
4 |
5 | import { cn } from "@/lib/utils";
6 |
7 | const buttonVariants = cva(
8 | "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium 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 cursor-pointer",
9 | {
10 | variants: {
11 | variant: {
12 | default: "bg-primary text-primary-foreground hover:bg-primary/90",
13 | destructive:
14 | "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
15 | outline:
16 | "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
17 | secondary:
18 | "bg-secondary text-secondary-foreground hover:bg-secondary/80",
19 | ghost:
20 | "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
21 | link: "text-primary underline-offset-4 hover:underline",
22 | },
23 | size: {
24 | default: "h-9 px-4 py-2 has-[>svg]:px-3",
25 | sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
26 | lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
27 | icon: "size-9",
28 | "icon-sm": "size-8",
29 | "icon-lg": "size-10",
30 | },
31 | },
32 | defaultVariants: {
33 | variant: "default",
34 | size: "default",
35 | },
36 | },
37 | );
38 |
39 | function Button({
40 | className,
41 | variant,
42 | size,
43 | asChild = false,
44 | ...props
45 | }: React.ComponentProps<"button"> &
46 | VariantProps & {
47 | asChild?: boolean;
48 | }) {
49 | const Comp = asChild ? Slot : "button";
50 |
51 | return (
52 |
57 | );
58 | }
59 |
60 | export { Button, buttonVariants };
61 |
--------------------------------------------------------------------------------
/registry/new-york/ui/github-stars-button.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { StarFilledIcon } from "@radix-ui/react-icons";
4 | import { useEffect, useState } from "react";
5 | import { Github } from "@/components/icons/social-icons";
6 | import { RaisedButton } from "@/registry/new-york/ui/raised-button";
7 |
8 | interface GitHubRepo {
9 | stargazers_count: number;
10 | html_url: string;
11 | name: string;
12 | full_name: string;
13 | }
14 |
15 | interface GitHubStarsButtonProps {
16 | repo: string;
17 | showLabel?: boolean;
18 | size?: "sm" | "default" | "lg";
19 | className?: string;
20 | }
21 |
22 | export function GitHubStarsButton({
23 | repo,
24 | showLabel = true,
25 | size = "sm",
26 | className,
27 | }: GitHubStarsButtonProps) {
28 | const [starCount, setStarCount] = useState(null);
29 | const [isLoading, setIsLoading] = useState(true);
30 |
31 | useEffect(() => {
32 | let isMounted = true;
33 |
34 | async function fetchStars() {
35 | try {
36 | const response = await fetch(`https://api.github.com/repos/${repo}`);
37 |
38 | if (!response.ok) {
39 | throw new Error("Failed to fetch repository data");
40 | }
41 |
42 | const data: GitHubRepo = await response.json();
43 |
44 | if (isMounted) {
45 | setStarCount(data.stargazers_count);
46 | setIsLoading(false);
47 | }
48 | } catch (error) {
49 | console.error("Error fetching GitHub stars:", error);
50 | if (isMounted) {
51 | setIsLoading(false);
52 | }
53 | }
54 | }
55 |
56 | fetchStars();
57 |
58 | return () => {
59 | isMounted = false;
60 | };
61 | }, [repo]);
62 |
63 | return (
64 |
69 |
74 |
75 |
76 | {showLabel && GitHub }
77 |
78 |
79 |
80 |
81 | {isLoading ? "..." : starCount || "0"}
82 |
83 |
84 |
85 |
86 | );
87 | }
88 |
--------------------------------------------------------------------------------
/components/previews/weather-card/with-forecast.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { WeatherCard } from "@/registry/new-york/ui/weather-card";
4 |
5 | const weatherDataWithForecast = {
6 | coord: { lon: 139.6917, lat: 35.6895 },
7 | weather: [
8 | {
9 | id: 803,
10 | main: "Clouds",
11 | description: "broken clouds",
12 | icon: "04d",
13 | },
14 | ],
15 | main: {
16 | temp: 18,
17 | feels_like: 17,
18 | temp_min: 16,
19 | temp_max: 20,
20 | pressure: 1015,
21 | humidity: 70,
22 | },
23 | visibility: 10000,
24 | wind: {
25 | speed: 4.5,
26 | deg: 150,
27 | },
28 | dt: 1699641600,
29 | sys: {
30 | country: "JP",
31 | sunrise: 1699566000,
32 | sunset: 1699604400,
33 | },
34 | timezone: 32400,
35 | name: "Tokyo",
36 | location: {
37 | city: "Tokyo",
38 | country: "Japan",
39 | region: null,
40 | },
41 | forecast: [
42 | {
43 | date: "2024-11-11",
44 | timestamp: 1699728000,
45 | temp_min: 16,
46 | temp_max: 22,
47 | humidity: 65,
48 | weather: {
49 | main: "Clear",
50 | description: "clear sky",
51 | icon: "01d",
52 | },
53 | },
54 | {
55 | date: "2024-11-12",
56 | timestamp: 1699814400,
57 | temp_min: 15,
58 | temp_max: 21,
59 | humidity: 68,
60 | weather: {
61 | main: "Clouds",
62 | description: "few clouds",
63 | icon: "02d",
64 | },
65 | },
66 | {
67 | date: "2024-11-13",
68 | timestamp: 1699900800,
69 | temp_min: 14,
70 | temp_max: 19,
71 | humidity: 72,
72 | weather: {
73 | main: "Rain",
74 | description: "light rain",
75 | icon: "10d",
76 | },
77 | },
78 | {
79 | date: "2024-11-14",
80 | timestamp: 1699987200,
81 | temp_min: 13,
82 | temp_max: 17,
83 | humidity: 75,
84 | weather: {
85 | main: "Rain",
86 | description: "moderate rain",
87 | icon: "10d",
88 | },
89 | },
90 | {
91 | date: "2024-11-15",
92 | timestamp: 1700073600,
93 | temp_min: 15,
94 | temp_max: 20,
95 | humidity: 70,
96 | weather: {
97 | main: "Clear",
98 | description: "clear sky",
99 | icon: "01d",
100 | },
101 | },
102 | ],
103 | };
104 |
105 | export default function WeatherCardWithForecast() {
106 | return (
107 |
108 |
109 |
110 | );
111 | }
112 |
--------------------------------------------------------------------------------
/components/previews/composer/default.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import {
4 | AttachmentIcon,
5 | HugeiconsIcon,
6 | Image01Icon,
7 | Link01Icon,
8 | } from "@/components/icons";
9 | import {
10 | Composer,
11 | type ComposerContextOption,
12 | type Tool,
13 | } from "@/registry/new-york/ui/composer";
14 |
15 | // Example tools for the slash command dropdown
16 | const exampleTools: Tool[] = [
17 | {
18 | name: "web_search",
19 | category: "search",
20 | description: "Search the web for information",
21 | },
22 | {
23 | name: "image_generation",
24 | category: "creative",
25 | description: "Generate images from text descriptions",
26 | },
27 | {
28 | name: "code_interpreter",
29 | category: "development",
30 | description: "Write and execute code",
31 | },
32 | {
33 | name: "file_analysis",
34 | category: "documents",
35 | description: "Analyze uploaded documents",
36 | },
37 | {
38 | name: "calendar_events",
39 | category: "productivity",
40 | description: "Manage calendar events",
41 | },
42 | {
43 | name: "email_compose",
44 | category: "communication",
45 | description: "Draft and send emails",
46 | },
47 | ];
48 |
49 | // Example context options for the plus button dropdown
50 | const contextOptions: ComposerContextOption[] = [
51 | {
52 | id: "attach",
53 | label: "Attach Files",
54 | description: "Upload documents, images, or other files",
55 | icon: ,
56 | onClick: () => console.log("Attach files clicked"),
57 | },
58 | {
59 | id: "image",
60 | label: "Add Image",
61 | description: "Upload or generate an image",
62 | icon: ,
63 | onClick: () => console.log("Add image clicked"),
64 | },
65 | {
66 | id: "link",
67 | label: "Add Link",
68 | description: "Paste a URL for analysis",
69 | icon: ,
70 | onClick: () => console.log("Add link clicked"),
71 | },
72 | ];
73 |
74 | export default function ComposerDefault() {
75 | return (
76 |
77 | {
82 | console.log("Message sent:", message);
83 | }}
84 | onToolSelect={(tool) => {
85 | console.log("Tool selected:", tool);
86 | }}
87 | />
88 |
89 | );
90 | }
91 |
--------------------------------------------------------------------------------
/registry/new-york/ui/message-bubble.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import "./message-bubble.css";
4 |
5 | import { cn } from "@/lib/utils";
6 |
7 | export interface MessageBubbleProps {
8 | message: string;
9 | variant?: "sent" | "received";
10 | grouped?: "first" | "middle" | "last" | "none";
11 | className?: string;
12 | children?: React.ReactNode;
13 | }
14 |
15 | export function MessageBubble({
16 | message,
17 | variant = "received",
18 | grouped = "none",
19 | className,
20 | children,
21 | }: MessageBubbleProps) {
22 | const groupedClasses =
23 | grouped === "first"
24 | ? "imessage-grouped-first mb-1.5"
25 | : grouped === "last"
26 | ? "imessage-grouped-last"
27 | : grouped === "middle"
28 | ? "imessage-grouped-middle mb-1.5"
29 | : "";
30 |
31 | return (
32 |
40 | {children ||
{message}
}
41 |
42 | );
43 | }
44 |
45 | export interface ChatMessageProps {
46 | timestamp?: string;
47 | messages: string[];
48 | variant?: "sent" | "received";
49 | className?: string;
50 | showTimestamp?: boolean;
51 | }
52 |
53 | export function ChatMessage({
54 | timestamp,
55 | messages,
56 | variant = "received",
57 | className,
58 | showTimestamp = true,
59 | }: ChatMessageProps) {
60 | const hasMultipleMessages = messages.length > 1;
61 |
62 | const getGroupedType = (index: number, total: number) => {
63 | if (total === 1) return "none";
64 | if (index === 0) return "first";
65 | if (index === total - 1) return "last";
66 | return "middle";
67 | };
68 |
69 | return (
70 |
71 |
72 | {messages.map((message, index) => (
73 |
83 | ))}
84 |
85 |
86 | {showTimestamp && timestamp && (
87 |
93 | {timestamp}
94 |
95 | )}
96 |
97 | );
98 | }
99 |
--------------------------------------------------------------------------------
/components/previews/notification-card/default.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import {
4 | type ActionType,
5 | NotificationCard,
6 | } from "@/registry/new-york/ui/notification-card";
7 |
8 | // Static dates for demo purposes - avoids React purity rules
9 | const DEMO_DATES = {
10 | fiveMinutesAgo: new Date("2025-12-15T10:50:00Z"),
11 | tenMinutesAgo: new Date("2025-12-15T10:45:00Z"),
12 | twoHoursAgo: new Date("2025-12-15T08:55:00Z"),
13 | };
14 |
15 | export default function NotificationCardDefault() {
16 | const handleAction = (
17 | notificationId: string,
18 | actionId: string,
19 | actionType: ActionType,
20 | ) => {
21 | console.log("Action:", { notificationId, actionId, actionType });
22 | };
23 |
24 | const notifications = [
25 | {
26 | id: "1",
27 | title: "New message from Alex",
28 | body: "Hey! Just wanted to check in on the project status. Can we schedule a quick call?",
29 | status: "unread" as const,
30 | createdAt: DEMO_DATES.fiveMinutesAgo,
31 | actions: [
32 | {
33 | id: "reply",
34 | label: "Reply",
35 | type: "modal" as ActionType,
36 | style: "primary" as const,
37 | },
38 | { id: "archive", label: "Archive", type: "api_call" as ActionType },
39 | ],
40 | },
41 | {
42 | id: "2",
43 | title: "Calendar event reminder",
44 | body: "Team standup in 15 minutes - Conference Room A",
45 | status: "unread" as const,
46 | createdAt: DEMO_DATES.tenMinutesAgo,
47 | actions: [
48 | {
49 | id: "join",
50 | label: "Join Meeting",
51 | type: "redirect" as ActionType,
52 | style: "primary" as const,
53 | },
54 | { id: "snooze", label: "Snooze", type: "api_call" as ActionType },
55 | ],
56 | },
57 | {
58 | id: "3",
59 | title: "Task completed",
60 | body: "Your scheduled workflow 'Weekly Report' has finished running.",
61 | status: "read" as const,
62 | createdAt: DEMO_DATES.twoHoursAgo,
63 | actions: [
64 | {
65 | id: "view",
66 | label: "View Report",
67 | type: "redirect" as ActionType,
68 | executed: true,
69 | },
70 | ],
71 | },
72 | ];
73 |
74 | return (
75 |
76 | {notifications.map((notification) => (
77 | console.log("Mark as read:", id)}
81 | onAction={handleAction}
82 | />
83 | ))}
84 |
85 | );
86 | }
87 |
--------------------------------------------------------------------------------
/lib/mdx.ts:
--------------------------------------------------------------------------------
1 | import path from "node:path";
2 | import fs from "fs";
3 | import matter from "gray-matter";
4 |
5 | const DOCS_PATH = path.join(process.cwd(), "content/docs");
6 |
7 | export interface TocEntry {
8 | id: string;
9 | text: string;
10 | level: number;
11 | }
12 |
13 | export interface DocMetadata {
14 | title: string;
15 | description: string;
16 | [key: string]: unknown;
17 | }
18 |
19 | /**
20 | * Extract table of contents from MDX content
21 | * Looks for heading patterns like ## Heading or ### Heading
22 | */
23 | export function extractTocFromMdx(content: string): TocEntry[] {
24 | const toc: TocEntry[] = [];
25 | const headingRegex = /^(#{2,4})\s+(.+)$/gm;
26 |
27 | for (const match of content.matchAll(headingRegex)) {
28 | const level = match[1].length;
29 | const text = match[2].trim();
30 |
31 | // Generate ID from heading text (same way rehype-slug does it)
32 | const id = text
33 | .toLowerCase()
34 | .replace(/[^a-z0-9\s-]/g, "")
35 | .replace(/\s+/g, "-")
36 | .replace(/-+/g, "-")
37 | .trim();
38 |
39 | toc.push({ id, text, level });
40 | }
41 |
42 | return toc;
43 | }
44 |
45 | /**
46 | * Get MDX file metadata and content
47 | */
48 | export function getMdxFile(slug: string[]) {
49 | const filePath = `${path.join(DOCS_PATH, ...slug)}.mdx`;
50 |
51 | if (!fs.existsSync(filePath)) {
52 | return null;
53 | }
54 |
55 | const fileContent = fs.readFileSync(filePath, "utf8");
56 | const { data, content } = matter(fileContent);
57 |
58 | return {
59 | metadata: data as DocMetadata,
60 | content,
61 | toc: extractTocFromMdx(content),
62 | };
63 | }
64 |
65 | /**
66 | * Get all available doc slugs for static generation
67 | */
68 | export function getAllDocSlugs(
69 | dir: string = DOCS_PATH,
70 | basePath: string[] = [],
71 | ): string[][] {
72 | const slugs: string[][] = [];
73 | const files = fs.readdirSync(dir);
74 |
75 | for (const file of files) {
76 | const filePath = path.join(dir, file);
77 | const stat = fs.statSync(filePath);
78 |
79 | if (stat.isDirectory()) {
80 | slugs.push(...getAllDocSlugs(filePath, [...basePath, file]));
81 | } else if (file.endsWith(".mdx")) {
82 | const slug = file.replace(/\.mdx$/, "");
83 | if (slug === "index") {
84 | // index.mdx maps to the base path
85 | if (basePath.length > 0) {
86 | slugs.push(basePath);
87 | }
88 | } else {
89 | slugs.push([...basePath, slug]);
90 | }
91 | }
92 | }
93 |
94 | return slugs;
95 | }
96 |
--------------------------------------------------------------------------------
/app/sitemap.ts:
--------------------------------------------------------------------------------
1 | import fs from "fs";
2 | import type { MetadataRoute } from "next";
3 | import { getAllDocSlugs } from "@/lib/mdx";
4 | import { siteConfig } from "@/lib/siteConfig";
5 | import registry from "@/registry.json";
6 |
7 | /**
8 | * Generate comprehensive dynamic sitemap for all pages
9 | * This file is automatically crawled by search engines
10 | * Includes homepage, docs, and all component pages
11 | */
12 | export default function sitemap(): MetadataRoute.Sitemap {
13 | const baseUrl = siteConfig.url;
14 | const routes: MetadataRoute.Sitemap = [];
15 |
16 | // Homepage - highest priority
17 | routes.push({
18 | url: baseUrl,
19 | lastModified: new Date(),
20 | changeFrequency: "monthly",
21 | priority: 1.0,
22 | });
23 |
24 | // Main documentation page
25 | routes.push({
26 | url: `${baseUrl}/docs`,
27 | lastModified: new Date(),
28 | changeFrequency: "weekly",
29 | priority: 0.9,
30 | });
31 |
32 | // Get all doc slugs and add to sitemap
33 | const docSlugs = getAllDocSlugs();
34 | const componentPaths = new Set();
35 |
36 | for (const slug of docSlugs) {
37 | if (slug.length === 0) continue; // Skip root docs (already added above)
38 |
39 | const path = `/docs/${slug.join("/")}`;
40 |
41 | // Track component paths separately for priority setting
42 | const isComponentPath = slug[0] === "components" && slug.length > 1;
43 | if (isComponentPath) {
44 | componentPaths.add(path);
45 | }
46 |
47 | // Try to get last modified date from file system
48 | let lastModified = new Date();
49 | try {
50 | const filePath = `${process.cwd()}/content/docs/${slug.join("/")}.mdx`;
51 | if (fs.existsSync(filePath)) {
52 | const stats = fs.statSync(filePath);
53 | lastModified = stats.mtime;
54 | }
55 | } catch {
56 | // Use current date if file not found
57 | }
58 |
59 | routes.push({
60 | url: `${baseUrl}${path}`,
61 | lastModified,
62 | changeFrequency: isComponentPath ? "weekly" : "monthly",
63 | priority: isComponentPath ? 0.8 : 0.7,
64 | });
65 | }
66 |
67 | // Ensure all components from registry are included
68 | // This handles any components that might not have MDX docs yet
69 | for (const item of registry.items) {
70 | const componentPath = `/docs/components/${item.name}`;
71 |
72 | // Only add if not already in sitemap
73 | if (!componentPaths.has(componentPath)) {
74 | routes.push({
75 | url: `${baseUrl}${componentPath}`,
76 | lastModified: new Date(),
77 | changeFrequency: "weekly",
78 | priority: 0.8,
79 | });
80 | }
81 | }
82 |
83 | return routes;
84 | }
85 |
--------------------------------------------------------------------------------
/content/docs/installation.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Installation
3 | description: How to install and configure GAIA UI in your project.
4 | ---
5 |
6 | ## Requirements
7 |
8 | - Node.js 18.17 or later
9 | - Next.js 13.4 or later
10 | - Tailwind CSS
11 |
12 | ## Initialize Your Project
13 |
14 | If you don't have a Next.js project yet, create one:
15 |
16 | ```bash
17 | npx create-next-app@latest my-app --typescript --tailwind --app
18 | cd my-app
19 | ```
20 |
21 | ## Install Dependencies
22 |
23 | GAIA UI uses the following dependencies:
24 |
25 | ```bash
26 | npm install class-variance-authority clsx tailwind-merge @hugeicons/react @hugeicons/core-free-icons
27 | npm install -D @types/node
28 | ```
29 |
30 | ## Configure Path Aliases
31 |
32 | Add the following to your `tsconfig.json`:
33 |
34 | ```json
35 | {
36 | "compilerOptions": {
37 | "baseUrl": ".",
38 | "paths": {
39 | "@/*": ["./*"]
40 | }
41 | }
42 | }
43 | ```
44 |
45 | ## Configure Tailwind CSS
46 |
47 | Update your `tailwind.config.ts`:
48 |
49 | ```ts
50 | import type { Config } from "tailwindcss";
51 |
52 | const config: Config = {
53 | darkMode: ["class"],
54 | content: [
55 | "./pages/**/*.{js,ts,jsx,tsx,mdx}",
56 | "./components/**/*.{js,ts,jsx,tsx,mdx}",
57 | "./app/**/*.{js,ts,jsx,tsx,mdx}",
58 | ],
59 | theme: {
60 | extend: {},
61 | },
62 | plugins: [],
63 | };
64 |
65 | export default config;
66 | ```
67 |
68 | ## Add Utility Functions
69 |
70 | Create a `lib/utils.ts` file:
71 |
72 | ```ts
73 | import { clsx, type ClassValue } from "clsx";
74 | import { twMerge } from "tailwind-merge";
75 |
76 | export function cn(...inputs: ClassValue[]) {
77 | return twMerge(clsx(inputs));
78 | }
79 | ```
80 |
81 | ## Install Components
82 |
83 | Now you can start installing components. For example, to install the Raised Button:
84 |
85 | ```bash
86 | npx shadcn@latest add https://ui.gaia.com/r/raised-button.json
87 | ```
88 |
89 | Or copy the component code directly from the [documentation](/docs/components/raised-button).
90 |
91 | ## Start Using Components
92 |
93 | Import and use components in your app:
94 |
95 | ```tsx
96 | import { RaisedButton } from "@/components/ui/raised-button";
97 |
98 | export default function App() {
99 | return Click me ;
100 | }
101 | ```
102 |
103 | ## Next Steps
104 |
105 | - Browse the [component library](/docs/components/raised-button)
106 | - Learn about [theming and customization](/docs/theming)
107 | - Join our [community](https://github.com/heygaia/ui)
108 |
--------------------------------------------------------------------------------
/components/core/component-preview-client.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as React from "react";
4 | import { codeToHtml } from "shiki";
5 | import { CodeBlock } from "@/components/core/code-block";
6 | import { FullscreenButton } from "@/components/core/fullscreen-button";
7 | import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
8 | import { cn } from "@/lib/utils";
9 |
10 | interface ComponentPreviewClientProps
11 | extends React.HTMLAttributes {
12 | children: React.ReactNode;
13 | code: string;
14 | }
15 |
16 | /**
17 | * Client component to render component preview with code tabs
18 | */
19 | export function ComponentPreviewClient({
20 | children,
21 | className,
22 | code,
23 | ...props
24 | }: ComponentPreviewClientProps) {
25 | const [html, setHtml] = React.useState("");
26 |
27 | React.useEffect(() => {
28 | codeToHtml(code, {
29 | lang: "tsx",
30 | themes: {
31 | light: "github-light",
32 | dark: "github-dark",
33 | },
34 | }).then(setHtml);
35 | }, [code]);
36 |
37 | return (
38 |
39 |
40 |
41 |
42 |
46 | Preview
47 |
48 |
52 | Code
53 |
54 |
55 |
59 |
60 | {children}
61 |
62 |
63 | {children}
64 |
65 |
66 |
67 |
68 |
72 |
76 |
77 |
78 |
79 |
80 |
81 | );
82 | }
83 |
--------------------------------------------------------------------------------
/components/ui/footer.tsx:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 | import { siteConfig } from "@/lib/siteConfig";
3 |
4 | export function Footer() {
5 | return (
6 |
7 |
8 |
9 | Made with ❤️ by
10 |
16 |
23 | Experience Company Logo
24 |
25 |
26 |
27 | The Experience Company.
28 |
29 |
30 | Source code available on
31 |
37 | GitHub
38 |
39 |
40 |
41 |
42 |
43 | );
44 | }
45 |
--------------------------------------------------------------------------------
/content/docs/components/calendar-event-card.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Calendar Event Card
3 | description: A styled card component for displaying calendar events with color indicators, status states, and action buttons.
4 | ---
5 |
6 |
7 |
8 | ## Features
9 |
10 | - **Color Indicator** - Colored left bar to categorize events
11 | - **Status States** - Idle, loading, and completed states for actions
12 | - **Action Buttons** - Confirm/complete actions with loading feedback
13 | - **Dotted Style** - Optional dotted border for pending/draft events
14 | - **Opacity Control** - Adjust opacity for disabled or completed states
15 | - **Composable Content** - Use helper components for consistent styling
16 |
17 | ## Installation
18 |
19 |
20 |
21 | Automatic
22 | Manual
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | Copy and paste the following code into your project.
32 |
33 |
34 |
35 | Install required dependencies:
36 |
37 | ```bash
38 | npm install @hugeicons/react @hugeicons/core-free-icons
39 | ```
40 |
41 |
42 |
43 |
44 | ## Usage
45 |
46 | ```tsx
47 | import {
48 | CalendarEventCard,
49 | EventTitle,
50 | EventTime,
51 | EventLocation,
52 | } from "@/components/ui/calendar-event-card";
53 |
54 | export default function Example() {
55 | return (
56 |
57 | Team Meeting
58 |
59 | Conference Room A
60 |
61 | );
62 | }
63 | ```
64 |
65 | ## Examples
66 |
67 | ### With Action Buttons
68 |
69 |
70 |
71 | ## Props
72 |
73 | ### CalendarEventCard
74 |
75 | | Prop | Type | Default | Description |
76 | | --- | --- | --- | --- |
77 | | eventColor | string | - | Color for the left indicator bar |
78 | | status | "idle" \| "loading" \| "completed" | "idle" | Action button state |
79 | | label | string | - | Optional label above content |
80 | | variant | "display" \| "action" | "display" | Show action button when "action" |
81 | | buttonColor | "primary" \| "danger" | "primary" | Action button color |
82 | | completedLabel | string | "Completed" | Button text when completed |
83 | | onAction | () => void | - | Action button click handler |
84 | | isDotted | boolean | false | Show dotted border style |
85 | | opacity | number | 1 | Card opacity (0-1) |
86 |
87 | ### Helper Components
88 |
89 | - `EventTitle` - Styled heading for event name
90 | - `EventTime` - Formatted time display with optional end time
91 | - `EventLocation` - Subtle location text
92 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@theexperiencecompany/gaia-ui",
3 | "description": "UI components specifically designed for building AI assistants and conversational interfaces built by the team behind the assistant",
4 | "version": "0.2.0",
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "build:pro": "USE_PRO_ICONS=true next build",
9 | "start": "next start",
10 | "lint": "biome check .",
11 | "lint:fix": "biome check --write .",
12 | "format": "biome format --write .",
13 | "format:check": "biome format .",
14 | "type": "tsc --noEmit",
15 | "registry:build": "shadcn build"
16 | },
17 | "publishConfig": {
18 | "access": "public"
19 | },
20 | "dependencies": {
21 | "@fontsource/inter": "^5.2.8",
22 | "@heroui/react": "^2.8.5",
23 | "@hugeicons/core-free-icons": "^2.0.0",
24 | "@hugeicons/react": "^1.1.1",
25 | "@mdx-js/loader": "^3.1.1",
26 | "@mdx-js/react": "^3.1.1",
27 | "@next/mdx": "^16.0.1",
28 | "@radix-ui/react-avatar": "^1.1.10",
29 | "@radix-ui/react-dialog": "^1.1.15",
30 | "@radix-ui/react-dropdown-menu": "^2.1.16",
31 | "@radix-ui/react-icons": "^1.3.2",
32 | "@radix-ui/react-label": "^2.1.7",
33 | "@radix-ui/react-navigation-menu": "^1.2.14",
34 | "@radix-ui/react-popover": "^1.1.15",
35 | "@radix-ui/react-scroll-area": "^1.2.10",
36 | "@radix-ui/react-separator": "^1.1.7",
37 | "@radix-ui/react-slot": "^1.2.3",
38 | "@radix-ui/react-tabs": "^1.1.13",
39 | "@radix-ui/react-toggle": "^1.1.10",
40 | "@radix-ui/react-tooltip": "^1.2.8",
41 | "@types/mdx": "^2.0.13",
42 | "class-variance-authority": "^0.7.1",
43 | "clsx": "^2.1.1",
44 | "cmdk": "^1.1.1",
45 | "date-fns": "^4.1.0",
46 | "emblor": "^1.4.8",
47 | "framer-motion": "^12.23.24",
48 | "fs": "0.0.1-security",
49 | "gray-matter": "^4.0.3",
50 | "little-date": "^1.0.0",
51 | "motion": "^12.23.24",
52 | "next": "16.0.10",
53 | "next-themes": "^0.4.6",
54 | "react": "19.1.0",
55 | "react-copy-to-clipboard": "^5.1.0",
56 | "react-day-picker": "^9.11.1",
57 | "react-dom": "19.1.0",
58 | "react-icons": "^5.5.0",
59 | "react-parallax-tilt": "^1.7.315",
60 | "rehype-autolink-headings": "^7.1.0",
61 | "rehype-pretty-code": "^0.14.1",
62 | "rehype-slug": "^6.0.0",
63 | "remark-frontmatter": "^5.0.0",
64 | "remark-gfm": "^4.0.1",
65 | "shiki": "^3.14.0",
66 | "tailwind-merge": "^3.3.1",
67 | "tw-animate-css": "^1.3.6",
68 | "zod": "^3.25.76"
69 | },
70 | "devDependencies": {
71 | "@eslint/eslintrc": "^3.3.1",
72 | "@tailwindcss/postcss": "^4.1.11",
73 | "@types/node": "^20.19.9",
74 | "@types/react": "19.1.2",
75 | "@types/react-dom": "19.1.2",
76 | "eslint": "^9.32.0",
77 | "eslint-config-next": "15.3.1",
78 | "eslint-plugin-react-hooks": "^7.0.1",
79 | "shadcn": "^3.0.0",
80 | "tailwindcss": "^4.1.11",
81 | "typescript": "^5.9.2"
82 | },
83 | "pnpm": {
84 | "overrides": {
85 | "@types/react": "19.1.2",
86 | "@types/react-dom": "19.1.2"
87 | }
88 | },
89 | "optionalDependencies": {
90 | "@hugeicons-pro/core-solid-rounded": "^2.0.0"
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/components/core/doc-page-layout.tsx:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 | import Link from "next/link";
3 | import * as React from "react";
4 | import { PageNavigation } from "@/components/core/page-navigation";
5 | import { TableOfContents } from "@/components/core/table-of-contents";
6 | import { ArrowRight02Icon, HugeiconsIcon } from "@/components/icons";
7 | import { getNavigation } from "@/lib/navigation";
8 |
9 | interface TocEntry {
10 | id: string;
11 | text: string;
12 | level: number;
13 | }
14 |
15 | interface DocPageLayoutProps {
16 | title: string;
17 | description: string;
18 | logo?: string;
19 | toc?: TocEntry[];
20 | markdownContent?: string;
21 | children: React.ReactNode;
22 | breadcrumbs?: { title: string; href: string }[];
23 | }
24 |
25 | export function DocPageLayout({
26 | title,
27 | description,
28 | logo,
29 | toc = [],
30 | markdownContent,
31 | children,
32 | breadcrumbs = [],
33 | }: DocPageLayoutProps) {
34 | const fullMarkdown = `# ${title}\n\n${description}\n\n${
35 | markdownContent || ""
36 | }`;
37 | const navigation = getNavigation();
38 |
39 | return (
40 |
41 |
42 | {breadcrumbs.length > 0 && (
43 |
44 |
48 | Docs
49 |
50 | {breadcrumbs.map((crumb) => (
51 |
52 |
53 |
57 | {crumb.title}
58 |
59 |
60 | ))}
61 |
62 | )}
63 | {logo && (
64 |
65 |
72 |
73 | )}
74 |
75 |
76 |
77 | {title}
78 |
79 |
84 |
85 |
{description}
86 |
87 |
{children}
88 |
93 |
94 |
99 |
100 | );
101 | }
102 |
--------------------------------------------------------------------------------
/content/docs/components/file-preview.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: File Preview
3 | description: Display uploaded files with smart type detection, file icons, image thumbnails, and upload progress indicators.
4 | ---
5 |
6 |
7 |
8 | ## Features
9 |
10 | - **Smart Type Detection** - Automatically detects 20+ file types
11 | - **Custom Icons** - Different icons for images, documents, code, media, archives
12 | - **Image Thumbnails** - Shows actual image previews for image files
13 | - **Upload Progress** - Loading spinner for files being uploaded
14 | - **Remove Button** - Hover-reveal delete button with callback
15 | - **File Type Badge** - Shows formatted file type (PDF, DOC, etc.)
16 |
17 | ## Installation
18 |
19 |
20 |
21 | Automatic
22 | Manual
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | Copy and paste the following code into your project.
32 |
33 |
34 |
35 | Install required dependencies:
36 |
37 | ```bash
38 | npm install @hugeicons/react @hugeicons/core-free-icons
39 | ```
40 |
41 |
42 |
43 |
44 | ## Usage
45 |
46 | ```tsx
47 | import { FilePreview } from "@/components/ui/file-preview";
48 |
49 | export default function Example() {
50 | const files = [
51 | {
52 | id: "1",
53 | url: "/uploads/document.pdf",
54 | name: "report.pdf",
55 | type: "application/pdf",
56 | },
57 | ];
58 |
59 | return (
60 | console.log("Remove:", id)}
63 | />
64 | );
65 | }
66 | ```
67 |
68 | ## Examples
69 |
70 | ### With Upload Progress
71 |
72 |
73 |
74 | ## Props
75 |
76 | ### FilePreview
77 |
78 | | Prop | Type | Description |
79 | | --- | --- | --- |
80 | | files | UploadedFile[] | Array of files to display |
81 | | onRemove | (id: string) => void | Callback when remove button is clicked |
82 | | className | string | Additional CSS classes |
83 |
84 | ### UploadedFile
85 |
86 | | Property | Type | Description |
87 | | --- | --- | --- |
88 | | id | string | Unique file identifier |
89 | | url | string | File URL (for image previews) |
90 | | name | string | File name |
91 | | type | string | MIME type (e.g., "image/png") |
92 | | description | string | Optional description |
93 | | isUploading | boolean | Show loading spinner |
94 |
95 | ## Supported File Types
96 |
97 | The component automatically detects and shows appropriate icons for:
98 |
99 | - **Images** - jpg, png, gif, webp, svg
100 | - **Documents** - pdf, doc, docx, odt, rtf
101 | - **Spreadsheets** - xls, xlsx, csv, ods
102 | - **Code** - js, ts, jsx, tsx, py, java, html, css, json, yaml
103 | - **Video** - mp4, avi, mov, mkv
104 | - **Audio** - mp3, wav, ogg
105 | - **Archives** - zip, rar, tar, gz, 7z
106 |
--------------------------------------------------------------------------------
/content/docs/components/notification-card.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Notification Card
3 | description: An enhanced notification card with action buttons, read/unread states, and timestamp display.
4 | ---
5 |
6 |
7 |
8 | ## Features
9 |
10 | - **Status States** - Unread, read, and archived visual states
11 | - **Action Buttons** - Multiple action types with icons (redirect, API call, workflow, modal)
12 | - **Action Styles** - Primary, danger, and default button styles
13 | - **Loading State** - Show loading spinner for async actions
14 | - **Executed State** - Visual indication when action has been completed
15 | - **Timestamps** - Smart relative time formatting (5m ago, 2h ago, etc.)
16 | - **Mark as Read** - One-click mark as read functionality
17 |
18 | ## Installation
19 |
20 |
21 |
22 | Automatic
23 | Manual
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | Copy and paste the following code into your project.
33 |
34 |
35 |
36 | Install required dependencies:
37 |
38 | ```bash
39 | npm install @hugeicons/react @hugeicons/core-free-icons
40 | ```
41 |
42 |
43 |
44 |
45 | ## Usage
46 |
47 | ```tsx
48 | import { NotificationCard } from "@/components/ui/notification-card";
49 |
50 | export default function Example() {
51 | return (
52 | {
63 | console.log(`Action ${actionId} on notification ${notifId}`);
64 | }}
65 | />
66 | );
67 | }
68 | ```
69 |
70 | ## Props
71 |
72 | ### NotificationCard
73 |
74 | | Prop | Type | Default | Description |
75 | | --- | --- | --- | --- |
76 | | id | string | - | Unique notification identifier |
77 | | title | string | - | Notification title |
78 | | body | string | - | Notification message |
79 | | status | "unread" \| "read" \| "archived" | "unread" | Read status |
80 | | createdAt | string \| Date | - | Creation timestamp |
81 | | actions | NotificationAction[] | [] | Available actions |
82 | | onMarkAsRead | (id) => void | - | Mark as read handler |
83 | | onAction | (notifId, actionId) => void | - | Action button handler |
84 | | loadingActionId | string | - | Action currently loading |
85 |
86 | ### NotificationAction
87 |
88 | | Property | Type | Description |
89 | | --- | --- | --- |
90 | | id | string | Action identifier |
91 | | label | string | Button text |
92 | | type | "redirect" \| "api_call" \| "workflow" \| "modal" | Action type (affects icon) |
93 | | style | "primary" \| "danger" \| "default" | Button color |
94 | | executed | boolean | Mark action as already executed |
95 |
--------------------------------------------------------------------------------
/registry/new-york/ui/message-bubble.css:
--------------------------------------------------------------------------------
1 | /* iOS-style message bubbles */
2 | .imessage-bubble {
3 | word-wrap: break-word;
4 | line-height: 24px;
5 | position: relative;
6 | padding: 8px 20px;
7 | max-width: 60vw;
8 | border-radius: 20px;
9 | }
10 |
11 | .imessage-bubble::before,
12 | .imessage-bubble::after {
13 | content: "";
14 | position: absolute;
15 | bottom: 0;
16 | height: 18px;
17 | }
18 |
19 | /* Grouped messages have tighter corner radii on the stacking side */
20 | /* Received messages (left side) - tight corners on left side */
21 | .imessage-grouped-first.imessage-from-them {
22 | /* biome-ignore lint/complexity/noImportantStyles: need to override radius */
23 | border-radius: 20px 20px 20px 5px !important;
24 | }
25 |
26 | .imessage-grouped-middle.imessage-from-them {
27 | /* biome-ignore lint/complexity/noImportantStyles: need to override radius */
28 | border-radius: 5px 20px 20px 5px !important;
29 | }
30 |
31 | .imessage-grouped-last.imessage-from-them {
32 | /* biome-ignore lint/complexity/noImportantStyles: need to override radius */
33 | border-radius: 5px 20px 20px 20px !important;
34 | }
35 |
36 | .imessage-grouped-last.imessage-from-them {
37 | /* biome-ignore lint/complexity/noImportantStyles: need to override radius */
38 | border-radius: 5px 20px 20px 20px !important;
39 | }
40 |
41 | /* Sent messages (right side) - tight corners on right side */
42 | .imessage-grouped-first.imessage-from-me {
43 | /* biome-ignore lint/complexity/noImportantStyles: need to override radius */
44 | border-radius: 20px 20px 5px 20px !important;
45 | }
46 |
47 | .imessage-grouped-middle.imessage-from-me {
48 | /* biome-ignore lint/complexity/noImportantStyles: need to override radius */
49 | border-radius: 20px 5px 5px 20px !important;
50 | }
51 |
52 | .imessage-grouped-last.imessage-from-me {
53 | /* biome-ignore lint/complexity/noImportantStyles: need to override radius */
54 | border-radius: 20px 5px 20px 20px !important;
55 | }
56 |
57 | /* Bot messages (from-them style) */
58 | .imessage-from-them {
59 | background-color: var(--color-zinc-300);
60 | color: black;
61 | align-self: flex-start;
62 | }
63 |
64 | .imessage-from-them::before {
65 | left: -7px;
66 | width: 20px;
67 | background-color: var(--color-zinc-300);
68 | border-bottom-right-radius: 16px;
69 | }
70 |
71 | .imessage-from-them::after {
72 | left: -26px;
73 | width: 26px;
74 | background-color: var(--color-background);
75 | border-bottom-right-radius: 10px;
76 | }
77 |
78 | /* User messages (from-me style) */
79 | .imessage-from-me {
80 | color: black;
81 | background: #00bbff;
82 | align-self: flex-end;
83 | }
84 |
85 | .imessage-from-me::before {
86 | right: -7px;
87 | width: 20px;
88 | background-color: #00bbff;
89 | border-bottom-left-radius: 16px 14px;
90 | }
91 |
92 | .imessage-from-me::after {
93 | right: -26px;
94 | width: 26px;
95 | background-color: var(--color-background);
96 | border-bottom-left-radius: 10px;
97 | }
98 |
99 | /* Grouped iMessage bubbles for NEW_MESSAGE_BREAK */
100 | /* Hide tails for grouped messages except the last one */
101 | .imessage-grouped-first::before,
102 | .imessage-grouped-first::after,
103 | .imessage-grouped-middle::before,
104 | .imessage-grouped-middle::after {
105 | display: none;
106 | }
107 |
--------------------------------------------------------------------------------
/content/docs/components/todo-item.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Todo Item
3 | description: An interactive todo item component with priority colors, due dates, labels, projects, and subtask support.
4 | ---
5 |
6 |
7 |
8 | ## Features
9 |
10 | - **Interactive Checkbox** - Toggle todo completion with visual feedback
11 | - **Priority Levels** - High, medium, low, and none with distinct color coding
12 | - **Due Dates** - Smart date formatting with overdue/today indicators
13 | - **Labels & Tags** - Color-coded labels for categorization
14 | - **Projects** - Link todos to projects with custom colors
15 | - **Subtasks** - Track progress with subtask completion count
16 | - **Click Handlers** - Custom handlers for clicks and completion toggles
17 |
18 | ## Installation
19 |
20 |
21 |
22 | Automatic
23 | Manual
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | Copy and paste the following code into your project.
33 |
34 |
35 |
36 | Install required dependencies:
37 |
38 | ```bash
39 | npm install @hugeicons/react @hugeicons/core-free-icons
40 | ```
41 |
42 |
43 |
44 |
45 | ## Usage
46 |
47 | ```tsx
48 | import { TodoItem } from "@/components/ui/todo-item";
49 |
50 | export default function Example() {
51 | return (
52 | {
60 | console.log(`Todo ${id} completed: ${completed}`);
61 | }}
62 | />
63 | );
64 | }
65 | ```
66 |
67 | ## Examples
68 |
69 | ### Priority Levels
70 |
71 |
72 |
73 | ## Props
74 |
75 | | Prop | Type | Description |
76 | | --- | --- | --- |
77 | | id | string | Unique identifier for the todo |
78 | | title | string | Todo title |
79 | | description | string | Optional description |
80 | | completed | boolean | Completion status |
81 | | priority | "high" \| "medium" \| "low" \| "none" | Priority level |
82 | | dueDate | string \| Date | Optional due date |
83 | | labels | TodoLabel[] | Array of labels |
84 | | subtasks | TodoSubtask[] | Array of subtasks |
85 | | project | TodoProject | Optional project assignment |
86 | | onToggleComplete | (id, completed) => void | Completion toggle handler |
87 | | onClick | (id) => void | Click handler |
88 | | isSelected | boolean | Selected state |
89 | | className | string | Additional CSS classes |
90 |
91 | ## Type Definitions
92 |
93 | ```typescript
94 | interface TodoLabel {
95 | id: string;
96 | name: string;
97 | color?: string;
98 | }
99 |
100 | interface TodoSubtask {
101 | id: string;
102 | title: string;
103 | completed: boolean;
104 | }
105 |
106 | interface TodoProject {
107 | id: string;
108 | name: string;
109 | color?: string;
110 | }
111 | ```
112 |
--------------------------------------------------------------------------------
/components/previews/slash-command-dropdown/default.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { Button } from "@heroui/react";
4 | import { useRef, useState } from "react";
5 | import { HugeiconsIcon, PlusSignIcon } from "@/components/icons";
6 | import {
7 | SlashCommandDropdown,
8 | type SlashCommandMatch,
9 | } from "@/registry/new-york/ui/slash-command-dropdown";
10 |
11 | // Sample tools data
12 | const TOOLS = [
13 | {
14 | name: "linear_create_issue",
15 | category: "linear",
16 | description: "Create a new issue in Linear",
17 | },
18 | {
19 | name: "linear_search_issues",
20 | category: "linear",
21 | description: "Search for existing issues",
22 | },
23 | {
24 | name: "github_pull_request",
25 | category: "github",
26 | description: "Create or view pull requests",
27 | },
28 | {
29 | name: "github_search_repos",
30 | category: "github",
31 | description: "Search repositories",
32 | },
33 | {
34 | name: "google_calendar_schedule",
35 | category: "google_calendar",
36 | description: "Schedule a meeting",
37 | },
38 | {
39 | name: "notion_create_page",
40 | category: "notion",
41 | description: "Create a new page in Notion",
42 | },
43 | {
44 | name: "slack_send_message",
45 | category: "slack",
46 | description: "Send a message to a channel",
47 | },
48 | {
49 | name: "gmail_send_email",
50 | category: "gmail",
51 | description: "Compose and send an email",
52 | },
53 | ];
54 |
55 | export default function SlashCommandDropdownPreview() {
56 | const [isVisible, setIsVisible] = useState(false);
57 | const [selectedCategory, setSelectedCategory] = useState("all");
58 | const [selectedIndex] = useState(0);
59 | const buttonRef = useRef(null);
60 |
61 | const matches: SlashCommandMatch[] = TOOLS.map((tool) => ({
62 | tool,
63 | score: 1,
64 | }));
65 |
66 | const handleSelect = (match: SlashCommandMatch) => {
67 | console.log("Selected:", match.tool.name);
68 | setIsVisible(false);
69 | };
70 |
71 | const toggleDropdown = () => {
72 | setIsVisible(!isVisible);
73 | };
74 |
75 | return (
76 |
77 |
78 |
79 | Click the button to open the tool menu
80 |
81 |
82 |
83 | }
88 | className="rounded-full"
89 | >
90 | Add Tool
91 |
92 |
93 | setIsVisible(false)}
98 | position={{
99 | top: undefined,
100 | left: 0,
101 | width: 320,
102 | }}
103 | isVisible={isVisible}
104 | openedViaButton={true}
105 | selectedCategory={selectedCategory}
106 | onCategoryChange={setSelectedCategory}
107 | className="absolute top-12 left-0 mt-2"
108 | style={{
109 | maxHeight: "300px",
110 | position: "absolute",
111 | }}
112 | />
113 |
114 |
115 |
116 | );
117 | }
118 |
--------------------------------------------------------------------------------
/content/docs/components/github-stars-button.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: GitHub Stars Button
3 | description: A beautiful button that displays your GitHub repository's star count with real-time data fetching.
4 | ---
5 |
6 |
7 |
8 | ### Sizes
9 |
10 |
11 |
12 | ### Variants
13 |
14 |
15 |
16 | ## Installation
17 |
18 |
19 |
20 | Automatic
21 | Manual
22 |
23 |
24 | {" "}
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | Copy and paste the following code into your project.
33 |
34 |
35 |
36 | Install required dependencies:
37 |
38 | ```bash
39 | pnpm add @radix-ui/react-icons
40 | ```
41 |
42 |
43 |
44 |
45 | ## Usage
46 |
47 | ```tsx
48 | import { GitHubStarsButton } from "@/components/ui/github-stars-button";
49 |
50 | export default function Example() {
51 | return ;
52 | }
53 | ```
54 |
55 | ## Examples
56 |
57 | ### Different Repositories
58 |
59 | ```tsx
60 |
61 |
62 |
63 | ```
64 |
65 | ### Without Label
66 |
67 | ```tsx
68 |
69 | ```
70 |
71 | ### Different Sizes
72 |
73 | ```tsx
74 |
75 |
76 |
77 | ```
78 |
79 | ### Custom Styling
80 |
81 | ```tsx
82 |
86 | ```
87 |
88 | ## Features
89 |
90 | - **Real-time Data**: Fetches star count from GitHub API on mount
91 | - **Loading State**: Shows "..." while loading star count
92 | - **Error Handling**: Gracefully handles fetch errors
93 | - **Click to Repository**: Opens the GitHub repository in a new tab
94 | - **Hover Effects**: Star icon turns yellow on hover
95 | - **Responsive**: Works on all screen sizes
96 | - **Lightweight**: No external dependencies except React and Radix Icons
97 |
98 | ## Props
99 |
100 | | Prop | Type | Default | Description |
101 | | --------- | ------------------------- | --------- | ------------------------------ |
102 | | repo | string | - | GitHub repository (owner/repo) |
103 | | showLabel | boolean | true | Whether to show "GitHub" label |
104 | | size | "sm" \| "default" \| "lg" | "sm" | Size of the button |
105 | | className | string | undefined | Additional CSS classes |
106 |
107 | ## Notes
108 |
109 | - The component uses GitHub's public API which has rate limiting (60 requests/hour for unauthenticated requests)
110 | - Star count is fetched once on component mount
111 | - The component automatically cleans up on unmount to prevent memory leaks
112 |
--------------------------------------------------------------------------------
/lib/navigation.ts:
--------------------------------------------------------------------------------
1 | import fs from "node:fs";
2 | import path from "node:path";
3 | import matter from "gray-matter";
4 | import type { NavItem, NavSection } from "@/types/nav-item";
5 |
6 | const DOCS_PATH = path.join(process.cwd(), "content/docs");
7 |
8 | /**
9 | * Get navigation items by scanning the docs directory and reading frontmatter
10 | */
11 | export function getNavigation(): NavSection[] {
12 | const sections: NavSection[] = [];
13 |
14 | // Getting Started section - always first
15 | const gettingStartedItems: NavItem[] = [];
16 |
17 | sections.push({
18 | title: "Socials",
19 | items: [
20 | {
21 | title: "GAIA",
22 | href: "https://heygaia.io",
23 | icon: "/media/logo.svg",
24 | },
25 | {
26 | title: "Discord",
27 | href: "https://discord.heygaia.io",
28 | },
29 | {
30 | title: "Twitter",
31 | href: "https://twitter.com/trygaia",
32 | },
33 | ],
34 | });
35 |
36 | // Add index (Introduction)
37 | const indexPath = path.join(DOCS_PATH, "index.mdx");
38 | if (fs.existsSync(indexPath)) {
39 | const fileContent = fs.readFileSync(indexPath, "utf8");
40 | const { data } = matter(fileContent);
41 | gettingStartedItems.push({
42 | title: data.title || "Introduction",
43 | href: "/docs",
44 | });
45 | }
46 |
47 | // Add other root-level docs
48 | const rootFiles = fs
49 | .readdirSync(DOCS_PATH)
50 | .filter((file) => file.endsWith(".mdx") && file !== "index.mdx");
51 |
52 | for (const file of rootFiles) {
53 | const filePath = path.join(DOCS_PATH, file);
54 | const fileContent = fs.readFileSync(filePath, "utf8");
55 | const { data } = matter(fileContent);
56 | const slug = file.replace(/\.mdx$/, "");
57 |
58 | gettingStartedItems.push({
59 | title: data.title || slug,
60 | href: `/docs/${slug}`,
61 | });
62 | }
63 |
64 | if (gettingStartedItems.length > 0) {
65 | sections.push({
66 | title: "Welcome",
67 | items: gettingStartedItems,
68 | });
69 | }
70 |
71 | // Scan for subdirectories (e.g., components/)
72 | const dirs = fs.readdirSync(DOCS_PATH).filter((file) => {
73 | const fullPath = path.join(DOCS_PATH, file);
74 | return fs.statSync(fullPath).isDirectory();
75 | });
76 |
77 | for (const dir of dirs) {
78 | const dirPath = path.join(DOCS_PATH, dir);
79 | const items: NavItem[] = [];
80 |
81 | // Read all MDX files in the directory
82 | const files = fs
83 | .readdirSync(dirPath)
84 | .filter((file) => file.endsWith(".mdx"));
85 |
86 | for (const file of files) {
87 | const filePath = path.join(dirPath, file);
88 | const fileContent = fs.readFileSync(filePath, "utf8");
89 | const { data } = matter(fileContent);
90 | const slug = file.replace(/\.mdx$/, "");
91 |
92 | items.push({
93 | title: data.title || slug,
94 | href: `/docs/${dir}/${slug}`,
95 | });
96 | }
97 |
98 | if (items.length > 0) {
99 | // Capitalize directory name for section title
100 | const sectionTitle = dir
101 | .split("-")
102 | .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
103 | .join(" ");
104 |
105 | sections.push({
106 | title: sectionTitle,
107 | items,
108 | });
109 | }
110 | }
111 |
112 | return sections;
113 | }
114 |
115 | /**
116 | * Main navigation items (for navbar)
117 | */
118 | export const mainNav = [
119 | {
120 | title: "Documentation",
121 | href: "/docs",
122 | },
123 | {
124 | title: "Components",
125 | href: "/docs/components",
126 | },
127 | ];
128 |
--------------------------------------------------------------------------------
/components/previews/search-results-tabs/default.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { MessageBubble } from "@/registry/new-york/ui/message-bubble";
4 | import { SearchResultsTabs } from "@/registry/new-york/ui/search-results-tabs";
5 |
6 | export default function SearchResultsTabsDefault() {
7 | const searchResults = {
8 | web: [
9 | {
10 | title: "Steve Jobs - Wikipedia",
11 | url: "https://en.wikipedia.org/wiki/Steve_Jobs",
12 | content:
13 | "Steven Paul Jobs (February 24, 1955 – October 5, 2011) was an American businessman, inventor, and investor best known for co-founding the technology company Apple Inc. Jobs was also the founder of NeXT and chairman and majority shareholder of Pixar.",
14 | score: 0.91813606,
15 | },
16 | {
17 | title:
18 | "Steve Jobs | Biography, Education, Apple, & Facts | Britannica Money",
19 | url: "https://www.britannica.com/money/Steve-Jobs",
20 | content:
21 | "Steve Jobs (born February 24, 1955, San Francisco, California, U.S.—died October 5, 2011, Palo Alto, California) was the cofounder of Apple Computer, Inc. With Steve Wozniak, Jobs founded Apple Inc. in 1976 and transformed the company into a world leader in telecommunications.",
22 | score: 0.818055,
23 | },
24 | {
25 | title: "Steve Jobs - Official Trailer (HD) - YouTube",
26 | url: "https://www.youtube.com/watch?v=aEr6K1bwIVs",
27 | content:
28 | "Set backstage at three iconic product launches and ending in 1998 with the unveiling of the iMac, Steve Jobs takes us behind the scenes of the digital revolution to paint an intimate portrait of the brilliant man at its epicenter.",
29 | score: 0.69968605,
30 | },
31 | {
32 | title: "Steve Jobs' 2005 Stanford Commencement Address - YouTube",
33 | url: "https://www.youtube.com/watch?v=UF8uR6Z6KLc",
34 | content:
35 | "Drawing from some of the most pivotal points in his life, Steve Jobs, chief executive officer and co-founder of Apple Computer and of Pixar",
36 | score: 0.6033015,
37 | },
38 | {
39 | title: "Steve Jobs (2015) - IMDb",
40 | url: "https://www.imdb.com/title/tt2080374/",
41 | content:
42 | "The story unfolds backstage at three iconic product launches, ending in 1998 with the unveiling of the iMac. Steve Jobs takes us behind the scenes of the digital revolution, to paint a portrait of the man at its epicenter.",
43 | score: 0.5683445,
44 | },
45 | ],
46 | images: [
47 | "https://cdn.britannica.com/04/171104-050-5B714956/Steve-Jobs-iPhone-2010.jpg",
48 | "https://cdn.mos.cms.futurecdn.net/GkeSqnHP3o4skUPJa7QSWZ.jpg",
49 | "https://hips.hearstapps.com/hmg-prod/images/apple-ceo-steve-jobs-speaks-during-an-apple-special-event-news-photo-1683661736.jpg",
50 | "https://hips.hearstapps.com/hmg-prod/images/apple-ceo-steve-jobs-speaks-during-an-apple-special-event-news-photo-1683661736.jpg?crop=0.800xw:0.563xh;0.0657xw,0.0147xh&resize=1200:*",
51 | "https://mg.co.za/wp-content/uploads/2023/03/stevejobs.1419962519.jpeg",
52 | ],
53 | news: [],
54 | };
55 |
56 | return (
57 |
58 | {/* Main component */}
59 |
60 |
61 | {/* iOS-style message bubbles */}
62 |
63 |
64 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | );
75 | }
76 |
--------------------------------------------------------------------------------
/public/r/github-stars-button.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3 | "name": "github-stars-button",
4 | "type": "registry:ui",
5 | "title": "GitHub Stars Button",
6 | "description": "A beautiful button that displays your GitHub repository's star count with real-time data fetching.",
7 | "dependencies": ["@radix-ui/react-icons"],
8 | "registryDependencies": ["raised-button"],
9 | "files": [
10 | {
11 | "path": "registry/new-york/ui/github-stars-button.tsx",
12 | "content": "\"use client\";\n\nimport { StarFilledIcon } from \"@radix-ui/react-icons\";\nimport { useEffect, useState } from \"react\";\n\nimport { RaisedButton } from \"@/registry/new-york/ui/raised-button\";\n\ninterface GitHubRepo {\n stargazers_count: number;\n html_url: string;\n name: string;\n full_name: string;\n}\n\ninterface GitHubStarsButtonProps {\n repo: string;\n showLabel?: boolean;\n size?: \"sm\" | \"default\" | \"lg\";\n className?: string;\n}\n\nexport function GitHubStarsButton({\n repo,\n showLabel = true,\n size = \"sm\",\n className,\n}: GitHubStarsButtonProps) {\n const [starCount, setStarCount] = useState(null);\n const [isLoading, setIsLoading] = useState(true);\n\n useEffect(() => {\n let isMounted = true;\n\n async function fetchStars() {\n try {\n const response = await fetch(`https://api.github.com/repos/${repo}`);\n\n if (!response.ok) {\n throw new Error(\"Failed to fetch repository data\");\n }\n\n const data: GitHubRepo = await response.json();\n\n if (isMounted) {\n setStarCount(data.stargazers_count);\n setIsLoading(false);\n }\n } catch (error) {\n console.error(\"Error fetching GitHub stars:\", error);\n if (isMounted) {\n setIsLoading(false);\n }\n }\n }\n\n fetchStars();\n\n return () => {\n isMounted = false;\n };\n }, [repo]);\n\n return (\n \n \n \n
\n \n \n {showLabel &&
GitHub }\n
\n \n \n \n {isLoading ? \"...\" : starCount || \"0\"}\n \n
\n \n \n );\n}\n",
13 | "type": "registry:ui"
14 | }
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/registry/new-york/ui/raised-button.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { cva, type VariantProps } from "class-variance-authority";
4 | import * as React from "react";
5 |
6 | import { cn } from "@/lib/utils";
7 | import {
8 | getContrastColor,
9 | getLuminance,
10 | parseColor,
11 | } from "@/lib/utils/colorUtils";
12 |
13 | const buttonVariants = cva(
14 | "inline-flex items-center justify-center dark:bg-zinc-500 dark:text-white whitespace-nowrap text-sm font-medium transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 relative bg-primary text-primary-foreground hover:bg-primary/90 border border-primary/50 shadow-md before:absolute before:inset-0 before:border-t before:border-white/40 before:bg-gradient-to-b before:from-white/20 before:to-transparent cursor-pointer transition-transform duration-200 active:scale-[0.96] subpixel-antialiased gap-2",
15 | {
16 | variants: {
17 | variant: {
18 | default: "",
19 | },
20 | size: {
21 | default: "h-10 px-4 py-2 rounded-xl before:rounded-xl",
22 | sm: "h-9 rounded-lg px-3 before:rounded-xl",
23 | lg: "h-11 rounded-lg px-8 before:rounded-lg",
24 | icon: "h-10 w-10",
25 | },
26 | },
27 | defaultVariants: {
28 | variant: "default",
29 | size: "default",
30 | },
31 | },
32 | );
33 |
34 | export interface ButtonProps
35 | extends React.ButtonHTMLAttributes,
36 | VariantProps {
37 | color?: string;
38 | }
39 |
40 | const RaisedButton = React.forwardRef(
41 | ({ className, variant, size, color, style = {}, ...props }, ref) => {
42 | const Comp = "button";
43 |
44 | const dynamicStyles = React.useMemo(() => {
45 | if (!color) return {};
46 |
47 | try {
48 | const rgb = parseColor(color);
49 | if (!rgb) return {};
50 |
51 | const luminance = getLuminance(rgb);
52 | const textColor = getContrastColor(luminance);
53 | const borderOpacity = 0.5;
54 | const hoverOpacity = 0.9;
55 | const whiteBorderOpacity = 0.6;
56 | const whiteGradientOpacity = 0.3;
57 | const shadowOpacity = 0.2;
58 | const shadowSpread = "0px";
59 | const shadowBlur = "5px";
60 |
61 | return {
62 | backgroundColor: color,
63 | color: textColor,
64 | borderColor: `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${borderOpacity})`,
65 | "--hover-bg": `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${hoverOpacity})`,
66 | "--border": `rgba(255, 255, 255, ${whiteBorderOpacity})`,
67 | "--gradient": `rgba(255, 255, 255, ${whiteGradientOpacity})`,
68 | "--shadow-color": `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${shadowOpacity})`,
69 | boxShadow: `0 4px ${shadowBlur} ${shadowSpread} var(--shadow-color)`,
70 | transition: "all 0.2s ease-in-out",
71 | };
72 | } catch (e) {
73 | console.error("Error processing color:", e);
74 | return {};
75 | }
76 | }, [color]);
77 |
78 | const computedClassName = cn(
79 | buttonVariants({ variant, size, className }),
80 | color &&
81 | "hover:bg-[color:var(--hover-bg)] before:border-[color:var(--border)] before:from-[color:var(--gradient)] hover:opacity-80 overflow-hidden",
82 | );
83 |
84 | return (
85 |
94 | );
95 | },
96 | );
97 | RaisedButton.displayName = "RaisedButton";
98 |
99 | export { buttonVariants, RaisedButton };
100 |
--------------------------------------------------------------------------------
/components/previews/nested-menu/default.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import {
4 | NestedMenu,
5 | type NestedMenuSectionProps,
6 | } from "@/registry/new-york/ui/nested-menu";
7 | import { Button } from "@/components/ui/button";
8 | import {
9 | HugeiconsIcon,
10 | Settings01Icon,
11 | BookOpen02Icon,
12 | CloudDownloadIcon,
13 | CustomerService01Icon,
14 | ArrowRight01Icon,
15 | MapsIcon,
16 | QuillWrite01Icon,
17 | BubbleChatQuestionIcon,
18 | GitPullRequestIcon,
19 | } from "@/components/icons";
20 |
21 | // Icon wrapper component for consistent styling
22 | const Icon = ({
23 | icon,
24 | className,
25 | }: {
26 | icon: typeof Settings01Icon;
27 | className?: string;
28 | }) => ;
29 |
30 | const menuSections: NestedMenuSectionProps[] = [
31 | {
32 | title: "Resources",
33 | items: [
34 | {
35 | key: "documentation",
36 | label: "Documentation",
37 | icon: (props) => ,
38 | onSelect: () => console.log("Documentation clicked"),
39 | },
40 | {
41 | key: "roadmap",
42 | label: "Roadmap",
43 | icon: (props) => ,
44 | onSelect: () => console.log("Roadmap clicked"),
45 | },
46 | {
47 | key: "blog",
48 | label: "Blog",
49 | icon: (props) => ,
50 | onSelect: () => console.log("Blog clicked"),
51 | },
52 | ],
53 | showDivider: true,
54 | },
55 | {
56 | items: [
57 | {
58 | key: "download",
59 | label: "Download",
60 | icon: (props) => ,
61 | hasSubmenu: true,
62 | submenuItems: [
63 | {
64 | key: "mac",
65 | label: "macOS",
66 | icon: (props) => ,
67 | onSelect: () => console.log("Download macOS"),
68 | },
69 | {
70 | key: "windows",
71 | label: "Windows",
72 | icon: (props) => ,
73 | onSelect: () => console.log("Download Windows"),
74 | },
75 | {
76 | key: "linux",
77 | label: "Linux",
78 | icon: (props) => ,
79 | onSelect: () => console.log("Download Linux"),
80 | },
81 | ],
82 | },
83 | {
84 | key: "support",
85 | label: "Support",
86 | icon: (props) => ,
87 | hasSubmenu: true,
88 | submenuItems: [
89 | {
90 | key: "contact",
91 | label: "Contact Support",
92 | icon: (props) => ,
93 | onSelect: () => console.log("Contact Support"),
94 | },
95 | {
96 | key: "feature",
97 | label: "Request a Feature",
98 | icon: (props) => ,
99 | onSelect: () => console.log("Request Feature"),
100 | },
101 | ],
102 | },
103 | ],
104 | },
105 | ];
106 |
107 | export default function NestedMenuDefault() {
108 | return (
109 |
110 |
114 |
115 | Open Menu
116 |
117 | }
118 | arrowIcon={(props) => }
119 | side="bottom"
120 | align="start"
121 | />
122 |
123 | );
124 | }
125 |
--------------------------------------------------------------------------------
/content/docs/components/workflow-card.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Workflow Card
3 | description: A card component for displaying workflow automations with rotated tool icons, execution stats, and action buttons.
4 | ---
5 |
6 |
7 |
8 | ## Features
9 |
10 | - **Rotated Tool Icons** - Eye-catching alternating rotation effect for category icons
11 | - **Multiple Variants** - User (run), explore (create), and suggestion variants
12 | - **Execution Stats** - Display run count with smart formatting (1.2K, 3.5M)
13 | - **Activation Status** - Show active/inactive state for user workflows
14 | - **Trigger Labels** - Display workflow trigger information
15 | - **Action Buttons** - Configurable action buttons with loading states
16 | - **Category Colors** - 10+ predefined categories with distinct color schemes
17 |
18 | ## Installation
19 |
20 |
21 |
22 | Automatic
23 | Manual
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | Copy and paste the following code into your project.
33 |
34 |
35 |
36 | Install required dependencies:
37 |
38 | ```bash
39 | npm install @hugeicons/react @hugeicons/core-free-icons
40 | ```
41 |
42 |
43 |
44 |
45 | ## Usage
46 |
47 | ```tsx
48 | import { WorkflowCard } from "@/components/ui/workflow-card";
49 |
50 | export default function Example() {
51 | return (
52 | console.log("Run workflow")}
62 | />
63 | );
64 | }
65 | ```
66 |
67 | ## Props
68 |
69 | ### WorkflowCard
70 |
71 | | Prop | Type | Default | Description |
72 | | --- | --- | --- | --- |
73 | | title | string | - | Workflow title |
74 | | description | string | - | Workflow description |
75 | | steps | WorkflowStep[] | - | Array of workflow steps |
76 | | totalExecutions | number | 0 | Total execution count |
77 | | isActivated | boolean | false | Activation status (user variant) |
78 | | triggerLabel | string | - | Trigger description |
79 | | variant | "user" \| "explore" \| "suggestion" | "explore" | Card variant |
80 | | actionLabel | string | - | Custom action button label |
81 | | isLoading | boolean | false | Show loading spinner |
82 | | onAction | () => void | - | Action button handler |
83 | | onClick | () => void | - | Card click handler |
84 |
85 | ### WorkflowStep
86 |
87 | | Property | Type | Description |
88 | | --- | --- | --- |
89 | | id | string | Step identifier |
90 | | title | string | Step title |
91 | | description | string | Optional description |
92 | | toolCategory | string | Category for icon/color |
93 |
94 | ## Supported Categories
95 |
96 | - `productivity` - Emerald
97 | - `documents` - Orange
98 | - `development` - Cyan
99 | - `memory` - Indigo
100 | - `creative` - Pink
101 | - `goal_tracking` - Emerald
102 | - `notifications` - Yellow
103 | - `email` - Blue
104 | - `calendar` - Purple
105 | - `search` - Teal
106 | - `general` - Gray
107 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # GAIA UI - Component Registry
4 |
5 | A collection of production-ready UI components designed specifically for building AI assistants and chatbots. These are the components we use at GAIA, now available for anyone building conversational interfaces.
6 |
7 |
8 |
9 |
10 |
11 |
12 | ## Why This Library?
13 |
14 | Building a great chat or assistant interface requires more than just generic UI components. We've spent countless hours refining these components to handle the unique challenges of conversational UIs.
15 |
16 | Instead of reinventing the wheel, use the same battle-tested components that power GAIA.
17 |
18 | ## Getting Started
19 |
20 | All components are compatible with the `shadcn` CLI, making installation effortless:
21 |
22 | ```bash
23 | npx shadcn@latest add https://ui.heygaia.io/r/navbar-menu.json
24 | ```
25 |
26 | Or add individual components:
27 |
28 | ```bash
29 | npx shadcn@latest add https://ui.heygaia.io/r/raised-button.json
30 | ```
31 |
32 | ### Base Components
33 |
34 | This registry uses standard shadcn/ui base components. Install them separately:
35 |
36 | ```bash
37 | npx shadcn@latest add button tooltip avatar dropdown-menu popover skeleton
38 | ```
39 |
40 | ## Documentation
41 |
42 | - **[Quick Start Guide](./QUICK_START.md)** - Get up and running in minutes
43 | - **[Registry Documentation](./REGISTRY.md)** - Complete guide for contributors
44 | - **[Component Docs](https://ui.heygaia.io)** - Live examples and API reference
45 |
46 | ## For Contributors
47 |
48 | ### Adding a New Component
49 |
50 | 1. Create your component in `registry/new-york/ui/`
51 | 2. Add entry to `registry.json`
52 | 3. Run `pnpm run registry:build`
53 | 4. Test with `npx shadcn@latest add http://localhost:3000/r/[component-name].json`
54 |
55 | See [REGISTRY.md](./REGISTRY.md) for detailed instructions.
56 |
57 | ### Development
58 |
59 | ```bash
60 | # Install dependencies
61 | pnpm install
62 |
63 | # Run dev server
64 | pnpm run dev
65 |
66 | # Build registry
67 | pnpm run registry:build
68 |
69 | # Type check
70 | pnpm run type
71 | ```
72 |
73 | ## Registry Structure
74 |
75 | ```
76 | registry/new-york/ui/ ← Components (shareable)
77 | lib/utils/ ← Utilities (shareable)
78 | public/r/ ← Generated JSON files
79 | registry.json ← Registry definition
80 | ```
81 |
82 | ## Built With
83 |
84 | - **Next.js 15** - For the documentation and registry
85 | - **Tailwind CSS v4** - For styling
86 | - **TypeScript** - For type safety
87 | - **Radix UI** - For accessible primitives
88 | - **Framer Motion** - For animations
89 | - **shadcn CLI** - For component installation
90 |
91 | ## Contributing
92 |
93 | We welcome contributions! Whether you're fixing bugs, improving documentation, or adding new components, we'd love your help.
94 |
95 | 1. Fork the repository
96 | 2. Create your feature branch (`git checkout -b feature/amazing-component`)
97 | 3. Follow the guidelines in [REGISTRY.md](./REGISTRY.md)
98 | 4. Test your component thoroughly
99 | 5. Submit a pull request
100 |
101 | ## License
102 |
103 | MIT License - feel free to use these components in your projects!
104 |
105 | ## Support
106 |
107 | - 🌐 [Website](https://ui.heygaia.io)
108 | - 🐙 [GitHub](https://github.com/theexperiencecompany/ui)
109 | - 🐦 [Twitter](https://twitter.com/trygaia)
110 |
111 | ---
112 |
113 | Built with ❤️ by the GAIA team
114 |
--------------------------------------------------------------------------------
/content/docs/components/author-tooltip.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Author Tooltip
3 | description: An interactive avatar with tooltip showing author information and social links.
4 | ---
5 |
6 |
7 |
8 | ## Sizes
9 |
10 |
11 |
12 | ## Installation
13 |
14 |
15 |
16 | Automatic
17 | Manual
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | Copy and paste the following code into your project.
27 |
28 |
29 |
30 | This component requires the Avatar and Tooltip components:
31 |
32 | ```bash
33 | npx shadcn@latest add avatar tooltip
34 | ```
35 |
36 |
37 |
38 |
39 | ## Usage
40 |
41 | ```tsx
42 | import { AuthorTooltip } from "@/components/ui/author-tooltip";
43 |
44 | export default function Example() {
45 | const author = {
46 | name: "Aryan",
47 | avatar: "https://github.com/aryanranderiya.png",
48 | role: "Founder & CEO",
49 | github: "https://github.com/aryanranderiya",
50 | twitter: "https://twitter.com/aryanranderiya",
51 | linkedin: "https://linkedin.com/in/aryanranderiya",
52 | };
53 |
54 | return ;
55 | }
56 | ```
57 |
58 | ### With Custom Trigger
59 |
60 | You can pass a custom trigger element instead of the default avatar:
61 |
62 | ```tsx
63 | Hover me}
66 | />
67 | ```
68 |
69 | ### With Additional Content
70 |
71 | Add extra content inside the tooltip with the `children` prop:
72 |
73 | ```tsx
74 |
75 |
76 |
Commits: 150
77 |
Additions: +5,000
78 |
79 |
80 | ```
81 |
82 | ## Props
83 |
84 | ### AuthorTooltip
85 |
86 | | Prop | Type | Default | Description |
87 | | --------------- | -------------------------- | ------------------------------------ | ----------------------------------------- |
88 | | author | Author | - | Author information object |
89 | | avatarSize | "sm" \| "md" \| "lg" \| "xl" | "sm" | Size of the avatar |
90 | | avatarClassName | string | "cursor-help border-2 border-border" | Additional CSS classes for avatar |
91 | | trigger | ReactNode | - | Custom trigger element (replaces avatar) |
92 | | children | ReactNode | - | Additional content to render in tooltip |
93 |
94 | ### Author Type
95 |
96 | | Property | Type | Required | Description |
97 | | -------- | ------ | -------- | ---------------------------- |
98 | | name | string | Yes | Author's full name |
99 | | avatar | string | Yes | URL to author's avatar image |
100 | | role | string | Yes | Author's role or title |
101 | | github | string | No | GitHub profile URL |
102 | | twitter | string | No | Twitter/X profile URL |
103 | | linkedin | string | No | LinkedIn profile URL |
104 |
105 |
--------------------------------------------------------------------------------
/public/r/author-tooltip.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3 | "name": "author-tooltip",
4 | "type": "registry:ui",
5 | "title": "Author Tooltip",
6 | "description": "An interactive avatar with tooltip showing author information and social links.",
7 | "dependencies": ["@hugeicons/react", "@hugeicons/core-free-icons"],
8 | "registryDependencies": [],
9 | "files": [
10 | {
11 | "path": "registry/new-york/ui/author-tooltip.tsx",
12 | "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { Avatar, AvatarFallback, AvatarImage } from \"@/components/ui/avatar\";\nimport {\n\tTooltip,\n\tTooltipContent,\n\tTooltipTrigger,\n} from \"@/components/ui/tooltip\";\nimport { cn } from \"@/lib/utils\";\nimport { FaGithub, FaLinkedin, FaTwitter } from \"react-icons/fa\";\n\nexport interface Author {\n\tname: string;\n\tavatar: string;\n\trole: string;\n\tlinkedin?: string;\n\ttwitter?: string;\n\tgithub?: string;\n}\n\ninterface AuthorTooltipProps {\n\tauthor: Author;\n\tavatarSize?: \"sm\" | \"md\" | \"lg\";\n\tavatarClassName?: string;\n}\n\nconst sizeMap = {\n\tsm: \"h-8 w-8\",\n\tmd: \"h-10 w-10\",\n\tlg: \"h-12 w-12\",\n};\n\nexport function AuthorTooltip({\n\tauthor,\n\tavatarSize = \"sm\",\n\tavatarClassName = \"cursor-help border-2 border-border\",\n}: AuthorTooltipProps) {\n\tconst getInitials = (name: string) => {\n\t\treturn name\n\t\t\t.split(\" \")\n\t\t\t.map((n) => n[0])\n\t\t\t.join(\"\")\n\t\t\t.toUpperCase()\n\t\t\t.slice(0, 2);\n\t};\n\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t \n\t\t\t\t\t{getInitials(author.name)} \n\t\t\t\t \n\t\t\t \n\t\t\t\n\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t \n\t\t\t\t\t\t{getInitials(author.name)} \n\t\t\t\t\t \n\t\t\t\t\t
\n\t\t\t\t\t\t{author.name} \n\t\t\t\t\t\t{author.role} \n\t\t\t\t\t
\n\t\t\t\t\t{(author.linkedin || author.twitter || author.github) && (\n\t\t\t\t\t\t
\n\t\t\t\t\t)}\n\t\t\t\t
\n\t\t\t \n\t\t \n\t);\n}\n",
13 | "type": "registry:ui"
14 | }
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/registry/new-york/ui/author-tooltip.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import type * as React from "react";
4 | import { FaGithub, FaLinkedin, FaTwitter } from "react-icons/fa";
5 | import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
6 | import {
7 | Tooltip,
8 | TooltipContent,
9 | TooltipTrigger,
10 | } from "@/components/ui/tooltip";
11 | import { cn } from "@/lib/utils";
12 |
13 | export interface Author {
14 | name: string;
15 | avatar: string;
16 | role: string;
17 | linkedin?: string;
18 | twitter?: string;
19 | github?: string;
20 | }
21 |
22 | interface AuthorTooltipProps {
23 | author: Author;
24 | avatarSize?: "sm" | "md" | "lg" | "xl";
25 | avatarClassName?: string;
26 | /** Custom trigger element - if not provided, defaults to avatar */
27 | trigger?: React.ReactNode;
28 | /** Additional content to render in the tooltip */
29 | children?: React.ReactNode;
30 | }
31 |
32 | const sizeMap = {
33 | sm: "h-8 w-8",
34 | md: "h-10 w-10",
35 | lg: "h-12 w-12",
36 | xl: "h-16 w-16",
37 | };
38 |
39 | export function AuthorTooltip({
40 | author,
41 | avatarSize = "sm",
42 | avatarClassName = "cursor-help border-2 border-border",
43 | trigger,
44 | children,
45 | }: AuthorTooltipProps) {
46 | const getInitials = (name: string) => {
47 | return name
48 | .split(" ")
49 | .map((n) => n[0])
50 | .join("")
51 | .toUpperCase()
52 | .slice(0, 2);
53 | };
54 |
55 | const defaultTrigger = (
56 |
57 |
58 | {getInitials(author.name)}
59 |
60 | );
61 |
62 | return (
63 |
64 |
65 | {trigger ? {trigger}
: defaultTrigger}
66 |
67 |
68 |
69 |
70 |
71 |
72 | {getInitials(author.name)}
73 |
74 |
75 | {author.name}
76 |
77 | {author.role}
78 |
79 |
80 | {(author.linkedin || author.twitter || author.github) && (
81 |
82 | {author.linkedin && (
83 |
89 |
90 |
91 | )}
92 | {author.twitter && (
93 |
99 |
100 |
101 | )}
102 | {author.github && (
103 |
109 |
110 |
111 | )}
112 |
113 | )}
114 |
115 | {children &&
{children}
}
116 |
117 |
118 |
119 | );
120 | }
121 |
--------------------------------------------------------------------------------
/components/core/page-navigation-client.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import Link from "next/link";
4 | import { usePathname } from "next/navigation";
5 | import { useState } from "react";
6 | import {
7 | ArrowLeft02Icon,
8 | ArrowRight02Icon,
9 | Copy01Icon,
10 | HugeiconsIcon,
11 | Tick02Icon,
12 | } from "@/components/icons";
13 | import { Button } from "@/components/ui/button";
14 | import type { NavSection } from "@/types/nav-item";
15 | import { Footer } from "../ui/footer";
16 |
17 | interface PageNavigationClientProps {
18 | position?: "top" | "bottom";
19 | markdownContent?: string;
20 | navigation: NavSection[];
21 | }
22 |
23 | export function PageNavigationClient({
24 | position = "top",
25 | markdownContent,
26 | navigation,
27 | }: PageNavigationClientProps) {
28 | const [copied, setCopied] = useState(false);
29 | const pathname = usePathname();
30 |
31 | const handleCopyPage = async () => {
32 | try {
33 | const contentToCopy = markdownContent || window.location.href;
34 | await navigator.clipboard.writeText(contentToCopy);
35 | setCopied(true);
36 | setTimeout(() => setCopied(false), 2000);
37 | } catch (err) {
38 | console.error("Failed to copy:", err);
39 | }
40 | };
41 |
42 | const allPages = navigation?.flatMap((section) =>
43 | section.items ? section.items : [],
44 | );
45 |
46 | const currentIndex = allPages?.findIndex((item) => item.href === pathname);
47 | const prevPage = currentIndex > 0 ? allPages[currentIndex - 1] : null;
48 | const nextPage =
49 | currentIndex >= 0 && currentIndex < allPages.length - 1
50 | ? allPages[currentIndex + 1]
51 | : null;
52 |
53 | if (position === "top") {
54 | return (
55 |
56 |
62 | {copied ? (
63 | <>
64 |
65 | Copied!
66 | >
67 | ) : (
68 | <>
69 |
70 | Copy page
71 | >
72 | )}
73 |
74 |
75 | {prevPage && (
76 |
82 |
83 |
84 |
85 |
86 | )}
87 | {nextPage && (
88 |
94 |
95 |
96 |
97 |
98 | )}
99 |
100 |
101 | );
102 | }
103 |
104 | return (
105 |
106 |
107 | {prevPage && (
108 |
109 |
110 |
111 | {prevPage.title}
112 |
113 |
114 | )}
115 | {nextPage && (
116 |
117 |
118 | {nextPage.title}
119 |
120 |
121 |
122 | )}
123 |
124 |
125 |
126 | );
127 | }
128 |
--------------------------------------------------------------------------------