chore: checkpoint before DAG views UX overhaul

This commit is contained in:
zenchantlive 2026-02-22 20:43:59 -08:00
parent 5695125a75
commit a03def1ca1
125 changed files with 40711 additions and 581 deletions

View file

@ -1 +0,0 @@
/mnt/c/Users/Zenchant/codex/beadboard/.agents/skills/shadcn-ui/

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,306 @@
### shadcn/ui Chart Component - Installation
Source: https://ui.shadcn.com/docs/components/chart
The chart component in shadcn/ui is built on Recharts, providing direct access to all Recharts capabilities with consistent theming.
```bash
npx shadcn@latest add chart
```
--------------------------------
### shadcn/ui Chart Component - Basic Usage
Source: https://ui.shadcn.com/docs/components/chart
The ChartContainer wraps your Recharts component and accepts a config prop for theming. Requires `min-h-[value]` for responsiveness.
```tsx
import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from "recharts"
import { ChartContainer, ChartTooltipContent } from "@/components/ui/chart"
const chartConfig = {
desktop: {
label: "Desktop",
color: "var(--chart-1)",
},
mobile: {
label: "Mobile",
color: "var(--chart-2)",
},
} satisfies import("@/components/ui/chart").ChartConfig
const chartData = [
{ month: "January", desktop: 186, mobile: 80 },
{ month: "February", desktop: 305, mobile: 200 },
{ month: "March", desktop: 237, mobile: 120 },
]
export function BarChartDemo() {
return (
<ChartContainer config={chartConfig} className="min-h-[200px] w-full">
<BarChart data={chartData}>
<CartesianGrid vertical={false} />
<XAxis dataKey="month" tickLine={false} axisLine={false} />
<Bar dataKey="desktop" fill="var(--color-desktop)" radius={4} />
<Bar dataKey="mobile" fill="var(--color-mobile)" radius={4} />
<ChartTooltip content={<ChartTooltipContent />} />
</BarChart>
</ChartContainer>
)
}
```
--------------------------------
### shadcn/ui Chart Component - ChartConfig with Custom Colors
Source: https://ui.shadcn.com/docs/components/chart
You can define custom colors directly in the configuration using hex values or CSS variables.
```tsx
const chartConfig = {
desktop: {
label: "Desktop",
color: "#2563eb",
theme: {
light: "#2563eb",
dark: "#60a5fa",
},
},
mobile: {
label: "Mobile",
color: "var(--chart-2)",
},
} satisfies import("@/components/ui/chart").ChartConfig
```
--------------------------------
### shadcn/ui Chart Component - CSS Variables
Source: https://ui.shadcn.com/docs/components/chart
Add chart color variables to your globals.css for consistent theming.
```css
:root {
/* Chart colors */
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.546 0.198 38.228);
--chart-4: oklch(0.596 0.151 343.253);
--chart-5: oklch(0.546 0.158 49.157);
}
.dark {
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.698 0.141 24.311);
--chart-4: oklch(0.676 0.172 171.196);
--chart-5: oklch(0.578 0.192 302.85);
}
```
--------------------------------
### shadcn/ui Chart Component - Line Chart Example
Source: https://ui.shadcn.com/docs/components/chart
Creating a line chart with shadcn/ui charts component.
```tsx
import { Line, LineChart, CartesianGrid, XAxis, YAxis } from "recharts"
import { ChartContainer, ChartTooltipContent } from "@/components/ui/chart"
const chartConfig = {
price: {
label: "Price",
color: "var(--chart-1)",
},
} satisfies import("@/components/ui/chart").ChartConfig
const chartData = [
{ month: "January", price: 186 },
{ month: "February", price: 305 },
{ month: "March", price: 237 },
{ month: "April", price: 203 },
{ month: "May", price: 276 },
]
export function LineChartDemo() {
return (
<ChartContainer config={chartConfig} className="min-h-[200px]">
<LineChart data={chartData}>
<CartesianGrid vertical={false} />
<XAxis dataKey="month" tickLine={false} axisLine={false} />
<YAxis tickLine={false} axisLine={false} tickFormatter={(value) => `$${value}`} />
<Line
dataKey="price"
stroke="var(--color-price)"
strokeWidth={2}
dot={false}
/>
<ChartTooltip content={<ChartTooltipContent />} />
</LineChart>
</ChartContainer>
)
}
```
--------------------------------
### shadcn/ui Chart Component - Area Chart Example
Source: https://ui.shadcn.com/docs/components/chart
Creating an area chart with gradient fill and legend.
```tsx
import { Area, AreaChart, XAxis, YAxis } from "recharts"
import {
ChartContainer,
ChartLegend,
ChartLegendContent,
ChartTooltipContent,
} from "@/components/ui/chart"
const chartConfig = {
desktop: { label: "Desktop", color: "var(--chart-1)" },
mobile: { label: "Mobile", color: "var(--chart-2)" },
} satisfies import("@/components/ui/chart").ChartConfig
export function AreaChartDemo() {
return (
<ChartContainer config={chartConfig} className="min-h-[200px]">
<AreaChart data={chartData}>
<XAxis dataKey="month" tickLine={false} axisLine={false} />
<YAxis tickLine={false} axisLine={false} />
<Area
dataKey="desktop"
fill="var(--color-desktop)"
stroke="var(--color-desktop)"
fillOpacity={0.3}
/>
<Area
dataKey="mobile"
fill="var(--color-mobile)"
stroke="var(--color-mobile)"
fillOpacity={0.3}
/>
<ChartTooltip content={<ChartTooltipContent />} />
<ChartLegend content={<ChartLegendContent />} />
</AreaChart>
</ChartContainer>
)
}
```
--------------------------------
### shadcn/ui Chart Component - Pie Chart Example
Source: https://ui.shadcn.com/docs/components/chart
Creating a pie/donut chart with shadcn/ui.
```tsx
import { Pie, PieChart } from "recharts"
import {
ChartContainer,
ChartLegend,
ChartLegendContent,
ChartTooltipContent,
} from "@/components/ui/chart"
const chartConfig = {
chrome: { label: "Chrome", color: "var(--chart-1)" },
safari: { label: "Safari", color: "var(--chart-2)" },
firefox: { label: "Firefox", color: "var(--chart-3)" },
} satisfies import("@/components/ui/chart").ChartConfig
const pieData = [
{ browser: "Chrome", visitors: 275, fill: "var(--color-chrome)" },
{ browser: "Safari", visitors: 200, fill: "var(--color-safari)" },
{ browser: "Firefox", visitors: 187, fill: "var(--color-firefox)" },
]
export function PieChartDemo() {
return (
<ChartContainer config={chartConfig} className="min-h-[200px]">
<PieChart>
<Pie
data={pieData}
dataKey="visitors"
nameKey="browser"
cx="50%"
cy="50%"
outerRadius={80}
/>
<ChartTooltip content={<ChartTooltipContent />} />
<ChartLegend content={<ChartLegendContent />} />
</PieChart>
</ChartContainer>
)
}
```
--------------------------------
### shadcn/ui ChartTooltipContent Props
Source: https://ui.shadcn.com/docs/components/chart
The ChartTooltipContent component accepts these props for customizing tooltip behavior.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `labelKey` | string | "label" | Key for tooltip label |
| `nameKey` | string | "name" | Key for tooltip name |
| `indicator` | "dot" \| "line" \| "dashed" | "dot" | Indicator style |
| `hideLabel` | boolean | false | Hide label |
| `hideIndicator` | boolean | false | Hide indicator |
--------------------------------
### shadcn/ui Chart Component - Accessibility
Source: https://ui.shadcn.com/docs/components/chart
Enable keyboard navigation and screen reader support by adding the accessibilityLayer prop.
```tsx
<BarChart accessibilityLayer data={chartData}>
<CartesianGrid vertical={false} />
<XAxis dataKey="month" />
<Bar dataKey="desktop" fill="var(--color-desktop)" />
<ChartTooltip content={<ChartTooltipContent />} />
</BarChart>
```
This adds:
- Keyboard arrow key navigation
- ARIA labels for chart elements
- Screen reader announcements for data values
--------------------------------
### shadcn/ui Chart Component - Recharts Dependencies
Source: https://ui.shadcn.com/docs/components/chart
The chart component requires the following Recharts dependencies to be installed.
```bash
pnpm add recharts
npm install recharts
yarn add recharts
```
Recharts provides the following chart types:
- Area, Bar, Line, Pie, Composed
- Radar, RadialBar, Scatter
- Funnel, Treemap

View file

@ -0,0 +1,145 @@
# shadcn/ui Learning Guide
This guide helps you learn shadcn/ui from basics to advanced patterns.
## Learning Path
### 1. Understanding the Philosophy
shadcn/ui is different from traditional component libraries:
- **Copy-paste components**: Components are copied into your project, not installed as packages
- **Full customization**: You own the code and can modify it freely
- **Built on Radix UI**: Provides accessibility primitives
- **Styled with Tailwind**: Uses utility classes for consistent styling
### 2. Core Concepts to Master
#### Class Variance Authority (CVA)
Most components use CVA for variant management:
```tsx
const buttonVariants = cva(
"base-classes",
{
variants: {
variant: {
default: "variant-classes",
destructive: "destructive-classes",
},
size: {
default: "size-classes",
sm: "small-classes",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
```
#### cn Utility Function
The `cn` function combines classes and resolves conflicts:
```tsx
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
```
### 3. Installation Checklist
- [ ] Initialize a new project (Next.js, Vite, or Remix)
- [ ] Install Tailwind CSS
- [ ] Run `npx shadcn@latest init`
- [ ] Configure CSS variables
- [ ] Install first component: `npx shadcn@latest add button`
### 4. Essential Components to Learn First
1. **Button** - Learn variants and sizes
2. **Input** - Form inputs with labels
3. **Card** - Container components
4. **Form** - Form handling with React Hook Form
5. **Dialog** - Modal windows
6. **Select** - Dropdown selections
7. **Toast** - Notifications
### 5. Common Patterns
#### Form Pattern
Every form follows this structure:
```tsx
1. Define Zod schema
2. Create form with useForm
3. Wrap with Form component
4. Add FormField for each input
5. Handle submission
```
#### Component Customization Pattern
To customize a component:
1. Copy component to your project
2. Modify the variants
3. Add new props if needed
4. Update types
### 6. Best Practices
- Always use TypeScript
- Follow the existing component structure
- Use semantic HTML when possible
- Test with screen readers for accessibility
- Keep components small and focused
### 7. Advanced Topics
- Creating custom components from scratch
- Building complex forms with validation
- Implementing dark mode
- Optimizing for performance
- Testing components
## Practice Exercises
### Exercise 1: Basic Setup
1. Create a new Next.js project
2. Set up shadcn/ui
3. Install and customize a Button component
4. Add a new variant "gradient"
### Exercise 2: Form Building
1. Create a contact form with:
- Name input (required)
- Email input (email validation)
- Message textarea (min length)
- Submit button with loading state
### Exercise 3: Component Combination
1. Build a settings page using:
- Card for layout
- Sheet for mobile menu
- Select for dropdowns
- Switch for toggles
- Toast for notifications
### Exercise 4: Custom Component
1. Create a custom Badge component
2. Support variants: default, secondary, destructive, outline
3. Support sizes: sm, default, lg
4. Add icon support
## Resources
- [Official Documentation](https://ui.shadcn.com)
- [GitHub Repository](https://github.com/shadcn/ui)
- [Examples Gallery](https://ui.shadcn.com/examples)
- [Radix UI Primitives](https://www.radix-ui.com/primitives)
- [Tailwind CSS Documentation](https://tailwindcss.com/docs)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,586 @@
# shadcn.io Component Library
shadcn.io is a comprehensive React UI component library built on shadcn/ui principles, providing developers with production-ready, composable components for modern web applications. The library serves as a centralized resource for React developers who need high-quality UI components with TypeScript support, ranging from basic interactive elements to advanced AI-powered integrations. Unlike traditional component libraries that require package installations, shadcn.io components are designed to be copied directly into your project, giving you full control and customization capabilities.
The library encompasses four major categories: composable UI components (terminal, dock, credit cards, QR codes, color pickers), chart components built with Recharts, animation components with Tailwind CSS integration, and custom React hooks for state management and lifecycle operations. Each component follows best practices for accessibility, performance, and developer experience, with comprehensive TypeScript definitions and Next.js compatibility. The platform emphasizes flexibility and customization, allowing developers to modify components at the source level rather than being constrained by package APIs.
## Core Components
### Terminal Component
Interactive terminal emulator with typing animations and command execution simulation for developer-focused interfaces.
```tsx
import { Terminal } from "@/components/ui/terminal"
export default function DemoTerminal() {
return (
npm install @repo/terminalInstalling dependencies...npm start
)
}
```
### Dock Component
macOS-style application dock with smooth magnification effects on hover, perfect for navigation menus.
```tsx
import { Dock, DockIcon } from "@/components/ui/dock"
import { Home, Settings, User, Mail } from "lucide-react"
export default function AppDock() {
return (
)
}
```
### Credit Card Component
Interactive 3D credit card component with flip animations for payment forms and card displays.
```tsx
import { CreditCard } from "@/components/ui/credit-card"
import { useState } from "react"
export default function PaymentForm() {
const [cardData, setCardData] = useState({
number: "4532 1234 5678 9010",
holder: "JOHN DOE",
expiry: "12/28",
cvv: "123"
})
return (
console.log("Card flipped:", flipped)}
/>
)
}
```
### Image Zoom Component
Zoomable image component with smooth modal transitions for image galleries and product displays.
```tsx
import { ImageZoom } from "@/components/ui/image-zoom"
export default function ProductGallery() {
return (
)
}
```
### QR Code Component
Generate and display customizable QR codes with styling options for links, contact information, and authentication.
```tsx
import { QRCode } from "@/components/ui/qr-code"
export default function ShareDialog() {
const shareUrl = "https://shadcn.io"
return (
Scan to visit shadcn.io
)
}
```
### Color Picker Component
Advanced color selection component supporting multiple color formats (HEX, RGB, HSL) with preview.
```tsx
import { ColorPicker } from "@/components/ui/color-picker"
import { useState } from "react"
export default function ThemeCustomizer() {
const [color, setColor] = useState("#3b82f6")
return (
Selected: {color}
)
}
```
## Chart Components
### Bar Chart Component
Clean bar chart component for data comparison and categorical analysis using Recharts.
```tsx
import { BarChart } from "@/components/ui/bar-chart"
export default function SalesChart() {
const data = [
{ month: "Jan", sales: 4000, revenue: 2400 },
{ month: "Feb", sales: 3000, revenue: 1398 },
{ month: "Mar", sales: 2000, revenue: 9800 },
{ month: "Apr", sales: 2780, revenue: 3908 },
{ month: "May", sales: 1890, revenue: 4800 },
{ month: "Jun", sales: 2390, revenue: 3800 }
]
return (
`$${value.toLocaleString()}`}
yAxisWidth={60}
/>
)
}
```
### Line Chart Component
Smooth line chart for visualizing trends and time-series data with multiple data series support.
```tsx
import { LineChart } from "@/components/ui/line-chart"
export default function MetricsChart() {
const data = [
{ date: "2024-01", users: 1200, sessions: 3400 },
{ date: "2024-02", users: 1800, sessions: 4200 },
{ date: "2024-03", users: 2400, sessions: 5800 },
{ date: "2024-04", users: 3100, sessions: 7200 },
{ date: "2024-05", users: 3800, sessions: 8900 }
]
return (
)
}
```
### Pie Chart Component
Donut chart component for displaying proportional data and percentage distributions.
```tsx
import { PieChart } from "@/components/ui/pie-chart"
export default function MarketShareChart() {
const data = [
{ name: "Product A", value: 400, fill: "#3b82f6" },
{ name: "Product B", value: 300, fill: "#10b981" },
{ name: "Product C", value: 300, fill: "#f59e0b" },
{ name: "Product D", value: 200, fill: "#ef4444" }
]
return (
`${entry.name}: ${entry.value}`}
/>
)
}
```
### Area Chart Component
Stacked area chart for visualizing volume changes over time with multiple data series.
```tsx
import { AreaChart } from "@/components/ui/area-chart"
export default function TrafficChart() {
const data = [
{ month: "Jan", mobile: 2000, desktop: 3000, tablet: 1000 },
{ month: "Feb", mobile: 2200, desktop: 3200, tablet: 1100 },
{ month: "Mar", mobile: 2800, desktop: 3800, tablet: 1300 },
{ month: "Apr", mobile: 3200, desktop: 4200, tablet: 1500 },
{ month: "May", mobile: 3800, desktop: 4800, tablet: 1800 }
]
return (
)
}
```
### Radar Chart Component
Multi-axis chart for comparing multiple variables across different categories simultaneously.
```tsx
import { RadarChart } from "@/components/ui/radar-chart"
export default function SkillsChart() {
const data = [
{ skill: "JavaScript", score: 85, industry: 75 },
{ skill: "TypeScript", score: 80, industry: 70 },
{ skill: "React", score: 90, industry: 80 },
{ skill: "Node.js", score: 75, industry: 72 },
{ skill: "CSS", score: 88, industry: 78 }
]
return (
)
}
```
### Mixed Chart Component
Combined bar and line chart for displaying multiple data types with different visualization methods.
```tsx
import { MixedChart } from "@/components/ui/mixed-chart"
export default function PerformanceChart() {
const data = [
{ month: "Jan", revenue: 4000, growth: 5.2 },
{ month: "Feb", revenue: 4200, growth: 5.0 },
{ month: "Mar", revenue: 4800, growth: 14.3 },
{ month: "Apr", revenue: 5200, growth: 8.3 },
{ month: "May", revenue: 5800, growth: 11.5 }
]
return (
)
}
```
## Animation Components
### Magnetic Effect Component
Magnetic hover effect that smoothly follows cursor movement for interactive buttons and cards.
```tsx
import { Magnetic } from "@/components/ui/magnetic"
export default function InteractiveButton() {
return (
Hover me
)
}
```
### Animated Cursor Component
Custom animated cursor with interactive effects and particle trails for immersive experiences.
```tsx
import { AnimatedCursor } from "@/components/ui/animated-cursor"
export default function Layout({ children }) {
return (
<>
{children}
)
}
```
### Apple Hello Effect Component
Recreation of Apple's iconic "hello" animation with multi-language text transitions.
```tsx
import { AppleHello } from "@/components/ui/apple-hello"
export default function WelcomeScreen() {
const greetings = [
{ text: "Hello", lang: "en" },
{ text: "Bonjour", lang: "fr" },
{ text: "こんにちは", lang: "ja" },
{ text: "Hola", lang: "es" },
{ text: "你好", lang: "zh" }
]
return (
)
}
```
### Liquid Button Component
Button with fluid liquid animation effect on hover for engaging call-to-action elements.
```tsx
import { LiquidButton } from "@/components/ui/liquid-button"
export default function CTASection() {
return (
console.log("CTA clicked")}
>
Get Started
)
}
```
### Rolling Text Component
Text animation that creates a rolling effect with smooth character transitions.
```tsx
import { RollingText } from "@/components/ui/rolling-text"
export default function AnimatedHeading() {
return (
)
}
```
### Shimmering Text Component
Text with animated shimmer effect for attention-grabbing headings and highlights.
```tsx
import { ShimmeringText } from "@/components/ui/shimmering-text"
export default function Hero() {
return (
)
}
```
## React Hooks
### useBoolean Hook
Enhanced boolean state management with toggle, enable, and disable methods for cleaner component logic.
```tsx
import { useBoolean } from "@/hooks/use-boolean"
export default function TogglePanel() {
const modal = useBoolean(false)
const loading = useBoolean(false)
const handleSubmit = async () => {
loading.setTrue()
try {
await submitForm()
modal.setFalse()
} finally {
loading.setFalse()
}
}
return (
<>
Toggle Modal
{modal.value && (
Status: {loading.value ? "Saving..." : "Ready"}
Submit
)}
)
}
```
### useCounter Hook
Counter hook with increment, decrement, reset, and set functionality for numeric state management.
```tsx
import { useCounter } from "@/hooks/use-counter"
export default function CartCounter() {
const quantity = useCounter(0, { min: 0, max: 99 })
return (
-
{quantity.value}
+
Reset
)
}
```
### useLocalStorage Hook
Persist state in browser localStorage with automatic serialization and deserialization.
```tsx
import { useLocalStorage } from "@/hooks/use-local-storage"
export default function UserPreferences() {
const [theme, setTheme] = useLocalStorage("theme", "light")
const [settings, setSettings] = useLocalStorage("settings", {
notifications: true,
emailUpdates: false
})
return (
setTheme(e.target.value)}>
LightDark setSettings({
...settings,
notifications: e.target.checked
})}
/>
Enable Notifications
)
}
```
### useDebounceValue Hook
Debounce values to prevent excessive updates and API calls during rapid user input.
```tsx
import { useDebounceValue } from "@/hooks/use-debounce-value"
import { useState, useEffect } from "react"
export default function SearchBox() {
const [search, setSearch] = useState("")
const debouncedSearch = useDebounceValue(search, 500)
const [results, setResults] = useState([])
const [apiCalls, setApiCalls] = useState(0)
useEffect(() => {
if (debouncedSearch) {
setApiCalls(prev => prev + 1)
fetch(`/api/search?q=${debouncedSearch}`)
.then(res => res.json())
.then(setResults)
}
}, [debouncedSearch])
return (
setSearch(e.target.value)}
placeholder="Search..."
/>
API calls: {apiCalls}
)
}
```
### useHover Hook
Track hover state on elements with customizable enter and leave delays for tooltip and preview functionality.
```tsx
import { useHover } from "@/hooks/use-hover"
import { useRef } from "react"
export default function ImagePreview() {
const hoverRef = useRef(null)
const isHovering = useHover(hoverRef, {
enterDelay: 200,
leaveDelay: 100
})
return (
![Preview](http://https:%2F%2Fcontext7.com%2Fwebsites%2Fshadcn_io%2Fllms.txt/thumbnail.jpg)
{isHovering && (
![Full size](http://https:%2F%2Fcontext7.com%2Fwebsites%2Fshadcn_io%2Fllms.txt/full-size.jpg)
)}
)
}
```
### useCountdown Hook
Countdown timer with play, pause, reset controls and completion callbacks for time-limited features.
```tsx
import { useCountdown } from "@/hooks/use-countdown"
export default function OTPTimer() {
const countdown = useCountdown({
initialSeconds: 60,
onComplete: () => alert("OTP expired! Request a new code.")
})
return (
{countdown.seconds}s
{!countdown.isRunning ? (
Start
) : (
Pause
)}
Reset
Status: {countdown.isComplete ? "Expired" : countdown.isRunning ? "Active" : "Paused"}
)
}
```
## Installation and Usage
### CLI Installation
Install components directly into your project using the shadcn CLI for instant integration.
```bash
# Initialize shadcn in your project
npx shadcn@latest init
# Add individual components
npx shadcn@latest add terminal
npx shadcn@latest add dock
npx shadcn@latest add credit-card
# Add multiple components at once
npx shadcn@latest add bar-chart line-chart pie-chart
# Add hooks
npx shadcn@latest add use-boolean use-counter use-local-storage
```
### Project Configuration
Configure your project to work with shadcn.io components using TypeScript and Tailwind CSS.
```typescript
// tailwind.config.ts
import type { Config } from "tailwindcss"
const config: Config = {
darkMode: ["class"],
content: [
"./pages/**/*.{ts,tsx}",
"./components/**/*.{ts,tsx}",
"./app/**/*.{ts,tsx}",
],
theme: {
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
},
},
},
plugins: [require("tailwindcss-animate")],
}
export default config
```
## Summary
The shadcn.io component library serves as a comprehensive toolkit for React developers building modern web applications with Next.js and TypeScript. The library's primary use cases include rapid prototyping of user interfaces, building data-rich dashboards with interactive charts, creating engaging user experiences with animations and effects, and implementing common UI patterns without writing boilerplate code. The copy-paste approach gives developers complete ownership of their components, allowing for deep customization while maintaining consistency with shadcn/ui design principles. Components are particularly well-suited for SaaS applications, admin panels, marketing websites, and e-commerce platforms that require professional, accessible UI elements.
Integration patterns center around composability and customization rather than rigid package dependencies. Developers can cherry-pick individual components using the CLI, modify them at the source level to match their design system, and combine them with existing shadcn/ui components for a cohesive interface. The library supports both light and dark themes through CSS variables, integrates seamlessly with Tailwind CSS utility classes, and follows React best practices for performance and accessibility. Custom hooks provide reusable logic patterns that complement the visual components, creating a complete ecosystem for building feature-rich applications. The TypeScript-first approach ensures type safety throughout the development process, while the Recharts integration for data visualization provides powerful charting capabilities without additional configuration overhead.

File diff suppressed because it is too large Load diff

View file

@ -1 +0,0 @@
/mnt/c/Users/Zenchant/codex/beadboard/.agents/skills/shadcn-ui/

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,306 @@
### shadcn/ui Chart Component - Installation
Source: https://ui.shadcn.com/docs/components/chart
The chart component in shadcn/ui is built on Recharts, providing direct access to all Recharts capabilities with consistent theming.
```bash
npx shadcn@latest add chart
```
--------------------------------
### shadcn/ui Chart Component - Basic Usage
Source: https://ui.shadcn.com/docs/components/chart
The ChartContainer wraps your Recharts component and accepts a config prop for theming. Requires `min-h-[value]` for responsiveness.
```tsx
import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from "recharts"
import { ChartContainer, ChartTooltipContent } from "@/components/ui/chart"
const chartConfig = {
desktop: {
label: "Desktop",
color: "var(--chart-1)",
},
mobile: {
label: "Mobile",
color: "var(--chart-2)",
},
} satisfies import("@/components/ui/chart").ChartConfig
const chartData = [
{ month: "January", desktop: 186, mobile: 80 },
{ month: "February", desktop: 305, mobile: 200 },
{ month: "March", desktop: 237, mobile: 120 },
]
export function BarChartDemo() {
return (
<ChartContainer config={chartConfig} className="min-h-[200px] w-full">
<BarChart data={chartData}>
<CartesianGrid vertical={false} />
<XAxis dataKey="month" tickLine={false} axisLine={false} />
<Bar dataKey="desktop" fill="var(--color-desktop)" radius={4} />
<Bar dataKey="mobile" fill="var(--color-mobile)" radius={4} />
<ChartTooltip content={<ChartTooltipContent />} />
</BarChart>
</ChartContainer>
)
}
```
--------------------------------
### shadcn/ui Chart Component - ChartConfig with Custom Colors
Source: https://ui.shadcn.com/docs/components/chart
You can define custom colors directly in the configuration using hex values or CSS variables.
```tsx
const chartConfig = {
desktop: {
label: "Desktop",
color: "#2563eb",
theme: {
light: "#2563eb",
dark: "#60a5fa",
},
},
mobile: {
label: "Mobile",
color: "var(--chart-2)",
},
} satisfies import("@/components/ui/chart").ChartConfig
```
--------------------------------
### shadcn/ui Chart Component - CSS Variables
Source: https://ui.shadcn.com/docs/components/chart
Add chart color variables to your globals.css for consistent theming.
```css
:root {
/* Chart colors */
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.546 0.198 38.228);
--chart-4: oklch(0.596 0.151 343.253);
--chart-5: oklch(0.546 0.158 49.157);
}
.dark {
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.698 0.141 24.311);
--chart-4: oklch(0.676 0.172 171.196);
--chart-5: oklch(0.578 0.192 302.85);
}
```
--------------------------------
### shadcn/ui Chart Component - Line Chart Example
Source: https://ui.shadcn.com/docs/components/chart
Creating a line chart with shadcn/ui charts component.
```tsx
import { Line, LineChart, CartesianGrid, XAxis, YAxis } from "recharts"
import { ChartContainer, ChartTooltipContent } from "@/components/ui/chart"
const chartConfig = {
price: {
label: "Price",
color: "var(--chart-1)",
},
} satisfies import("@/components/ui/chart").ChartConfig
const chartData = [
{ month: "January", price: 186 },
{ month: "February", price: 305 },
{ month: "March", price: 237 },
{ month: "April", price: 203 },
{ month: "May", price: 276 },
]
export function LineChartDemo() {
return (
<ChartContainer config={chartConfig} className="min-h-[200px]">
<LineChart data={chartData}>
<CartesianGrid vertical={false} />
<XAxis dataKey="month" tickLine={false} axisLine={false} />
<YAxis tickLine={false} axisLine={false} tickFormatter={(value) => `$${value}`} />
<Line
dataKey="price"
stroke="var(--color-price)"
strokeWidth={2}
dot={false}
/>
<ChartTooltip content={<ChartTooltipContent />} />
</LineChart>
</ChartContainer>
)
}
```
--------------------------------
### shadcn/ui Chart Component - Area Chart Example
Source: https://ui.shadcn.com/docs/components/chart
Creating an area chart with gradient fill and legend.
```tsx
import { Area, AreaChart, XAxis, YAxis } from "recharts"
import {
ChartContainer,
ChartLegend,
ChartLegendContent,
ChartTooltipContent,
} from "@/components/ui/chart"
const chartConfig = {
desktop: { label: "Desktop", color: "var(--chart-1)" },
mobile: { label: "Mobile", color: "var(--chart-2)" },
} satisfies import("@/components/ui/chart").ChartConfig
export function AreaChartDemo() {
return (
<ChartContainer config={chartConfig} className="min-h-[200px]">
<AreaChart data={chartData}>
<XAxis dataKey="month" tickLine={false} axisLine={false} />
<YAxis tickLine={false} axisLine={false} />
<Area
dataKey="desktop"
fill="var(--color-desktop)"
stroke="var(--color-desktop)"
fillOpacity={0.3}
/>
<Area
dataKey="mobile"
fill="var(--color-mobile)"
stroke="var(--color-mobile)"
fillOpacity={0.3}
/>
<ChartTooltip content={<ChartTooltipContent />} />
<ChartLegend content={<ChartLegendContent />} />
</AreaChart>
</ChartContainer>
)
}
```
--------------------------------
### shadcn/ui Chart Component - Pie Chart Example
Source: https://ui.shadcn.com/docs/components/chart
Creating a pie/donut chart with shadcn/ui.
```tsx
import { Pie, PieChart } from "recharts"
import {
ChartContainer,
ChartLegend,
ChartLegendContent,
ChartTooltipContent,
} from "@/components/ui/chart"
const chartConfig = {
chrome: { label: "Chrome", color: "var(--chart-1)" },
safari: { label: "Safari", color: "var(--chart-2)" },
firefox: { label: "Firefox", color: "var(--chart-3)" },
} satisfies import("@/components/ui/chart").ChartConfig
const pieData = [
{ browser: "Chrome", visitors: 275, fill: "var(--color-chrome)" },
{ browser: "Safari", visitors: 200, fill: "var(--color-safari)" },
{ browser: "Firefox", visitors: 187, fill: "var(--color-firefox)" },
]
export function PieChartDemo() {
return (
<ChartContainer config={chartConfig} className="min-h-[200px]">
<PieChart>
<Pie
data={pieData}
dataKey="visitors"
nameKey="browser"
cx="50%"
cy="50%"
outerRadius={80}
/>
<ChartTooltip content={<ChartTooltipContent />} />
<ChartLegend content={<ChartLegendContent />} />
</PieChart>
</ChartContainer>
)
}
```
--------------------------------
### shadcn/ui ChartTooltipContent Props
Source: https://ui.shadcn.com/docs/components/chart
The ChartTooltipContent component accepts these props for customizing tooltip behavior.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `labelKey` | string | "label" | Key for tooltip label |
| `nameKey` | string | "name" | Key for tooltip name |
| `indicator` | "dot" \| "line" \| "dashed" | "dot" | Indicator style |
| `hideLabel` | boolean | false | Hide label |
| `hideIndicator` | boolean | false | Hide indicator |
--------------------------------
### shadcn/ui Chart Component - Accessibility
Source: https://ui.shadcn.com/docs/components/chart
Enable keyboard navigation and screen reader support by adding the accessibilityLayer prop.
```tsx
<BarChart accessibilityLayer data={chartData}>
<CartesianGrid vertical={false} />
<XAxis dataKey="month" />
<Bar dataKey="desktop" fill="var(--color-desktop)" />
<ChartTooltip content={<ChartTooltipContent />} />
</BarChart>
```
This adds:
- Keyboard arrow key navigation
- ARIA labels for chart elements
- Screen reader announcements for data values
--------------------------------
### shadcn/ui Chart Component - Recharts Dependencies
Source: https://ui.shadcn.com/docs/components/chart
The chart component requires the following Recharts dependencies to be installed.
```bash
pnpm add recharts
npm install recharts
yarn add recharts
```
Recharts provides the following chart types:
- Area, Bar, Line, Pie, Composed
- Radar, RadialBar, Scatter
- Funnel, Treemap

View file

@ -0,0 +1,145 @@
# shadcn/ui Learning Guide
This guide helps you learn shadcn/ui from basics to advanced patterns.
## Learning Path
### 1. Understanding the Philosophy
shadcn/ui is different from traditional component libraries:
- **Copy-paste components**: Components are copied into your project, not installed as packages
- **Full customization**: You own the code and can modify it freely
- **Built on Radix UI**: Provides accessibility primitives
- **Styled with Tailwind**: Uses utility classes for consistent styling
### 2. Core Concepts to Master
#### Class Variance Authority (CVA)
Most components use CVA for variant management:
```tsx
const buttonVariants = cva(
"base-classes",
{
variants: {
variant: {
default: "variant-classes",
destructive: "destructive-classes",
},
size: {
default: "size-classes",
sm: "small-classes",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
```
#### cn Utility Function
The `cn` function combines classes and resolves conflicts:
```tsx
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
```
### 3. Installation Checklist
- [ ] Initialize a new project (Next.js, Vite, or Remix)
- [ ] Install Tailwind CSS
- [ ] Run `npx shadcn@latest init`
- [ ] Configure CSS variables
- [ ] Install first component: `npx shadcn@latest add button`
### 4. Essential Components to Learn First
1. **Button** - Learn variants and sizes
2. **Input** - Form inputs with labels
3. **Card** - Container components
4. **Form** - Form handling with React Hook Form
5. **Dialog** - Modal windows
6. **Select** - Dropdown selections
7. **Toast** - Notifications
### 5. Common Patterns
#### Form Pattern
Every form follows this structure:
```tsx
1. Define Zod schema
2. Create form with useForm
3. Wrap with Form component
4. Add FormField for each input
5. Handle submission
```
#### Component Customization Pattern
To customize a component:
1. Copy component to your project
2. Modify the variants
3. Add new props if needed
4. Update types
### 6. Best Practices
- Always use TypeScript
- Follow the existing component structure
- Use semantic HTML when possible
- Test with screen readers for accessibility
- Keep components small and focused
### 7. Advanced Topics
- Creating custom components from scratch
- Building complex forms with validation
- Implementing dark mode
- Optimizing for performance
- Testing components
## Practice Exercises
### Exercise 1: Basic Setup
1. Create a new Next.js project
2. Set up shadcn/ui
3. Install and customize a Button component
4. Add a new variant "gradient"
### Exercise 2: Form Building
1. Create a contact form with:
- Name input (required)
- Email input (email validation)
- Message textarea (min length)
- Submit button with loading state
### Exercise 3: Component Combination
1. Build a settings page using:
- Card for layout
- Sheet for mobile menu
- Select for dropdowns
- Switch for toggles
- Toast for notifications
### Exercise 4: Custom Component
1. Create a custom Badge component
2. Support variants: default, secondary, destructive, outline
3. Support sizes: sm, default, lg
4. Add icon support
## Resources
- [Official Documentation](https://ui.shadcn.com)
- [GitHub Repository](https://github.com/shadcn/ui)
- [Examples Gallery](https://ui.shadcn.com/examples)
- [Radix UI Primitives](https://www.radix-ui.com/primitives)
- [Tailwind CSS Documentation](https://tailwindcss.com/docs)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,586 @@
# shadcn.io Component Library
shadcn.io is a comprehensive React UI component library built on shadcn/ui principles, providing developers with production-ready, composable components for modern web applications. The library serves as a centralized resource for React developers who need high-quality UI components with TypeScript support, ranging from basic interactive elements to advanced AI-powered integrations. Unlike traditional component libraries that require package installations, shadcn.io components are designed to be copied directly into your project, giving you full control and customization capabilities.
The library encompasses four major categories: composable UI components (terminal, dock, credit cards, QR codes, color pickers), chart components built with Recharts, animation components with Tailwind CSS integration, and custom React hooks for state management and lifecycle operations. Each component follows best practices for accessibility, performance, and developer experience, with comprehensive TypeScript definitions and Next.js compatibility. The platform emphasizes flexibility and customization, allowing developers to modify components at the source level rather than being constrained by package APIs.
## Core Components
### Terminal Component
Interactive terminal emulator with typing animations and command execution simulation for developer-focused interfaces.
```tsx
import { Terminal } from "@/components/ui/terminal"
export default function DemoTerminal() {
return (
npm install @repo/terminalInstalling dependencies...npm start
)
}
```
### Dock Component
macOS-style application dock with smooth magnification effects on hover, perfect for navigation menus.
```tsx
import { Dock, DockIcon } from "@/components/ui/dock"
import { Home, Settings, User, Mail } from "lucide-react"
export default function AppDock() {
return (
)
}
```
### Credit Card Component
Interactive 3D credit card component with flip animations for payment forms and card displays.
```tsx
import { CreditCard } from "@/components/ui/credit-card"
import { useState } from "react"
export default function PaymentForm() {
const [cardData, setCardData] = useState({
number: "4532 1234 5678 9010",
holder: "JOHN DOE",
expiry: "12/28",
cvv: "123"
})
return (
console.log("Card flipped:", flipped)}
/>
)
}
```
### Image Zoom Component
Zoomable image component with smooth modal transitions for image galleries and product displays.
```tsx
import { ImageZoom } from "@/components/ui/image-zoom"
export default function ProductGallery() {
return (
)
}
```
### QR Code Component
Generate and display customizable QR codes with styling options for links, contact information, and authentication.
```tsx
import { QRCode } from "@/components/ui/qr-code"
export default function ShareDialog() {
const shareUrl = "https://shadcn.io"
return (
Scan to visit shadcn.io
)
}
```
### Color Picker Component
Advanced color selection component supporting multiple color formats (HEX, RGB, HSL) with preview.
```tsx
import { ColorPicker } from "@/components/ui/color-picker"
import { useState } from "react"
export default function ThemeCustomizer() {
const [color, setColor] = useState("#3b82f6")
return (
Selected: {color}
)
}
```
## Chart Components
### Bar Chart Component
Clean bar chart component for data comparison and categorical analysis using Recharts.
```tsx
import { BarChart } from "@/components/ui/bar-chart"
export default function SalesChart() {
const data = [
{ month: "Jan", sales: 4000, revenue: 2400 },
{ month: "Feb", sales: 3000, revenue: 1398 },
{ month: "Mar", sales: 2000, revenue: 9800 },
{ month: "Apr", sales: 2780, revenue: 3908 },
{ month: "May", sales: 1890, revenue: 4800 },
{ month: "Jun", sales: 2390, revenue: 3800 }
]
return (
`$${value.toLocaleString()}`}
yAxisWidth={60}
/>
)
}
```
### Line Chart Component
Smooth line chart for visualizing trends and time-series data with multiple data series support.
```tsx
import { LineChart } from "@/components/ui/line-chart"
export default function MetricsChart() {
const data = [
{ date: "2024-01", users: 1200, sessions: 3400 },
{ date: "2024-02", users: 1800, sessions: 4200 },
{ date: "2024-03", users: 2400, sessions: 5800 },
{ date: "2024-04", users: 3100, sessions: 7200 },
{ date: "2024-05", users: 3800, sessions: 8900 }
]
return (
)
}
```
### Pie Chart Component
Donut chart component for displaying proportional data and percentage distributions.
```tsx
import { PieChart } from "@/components/ui/pie-chart"
export default function MarketShareChart() {
const data = [
{ name: "Product A", value: 400, fill: "#3b82f6" },
{ name: "Product B", value: 300, fill: "#10b981" },
{ name: "Product C", value: 300, fill: "#f59e0b" },
{ name: "Product D", value: 200, fill: "#ef4444" }
]
return (
`${entry.name}: ${entry.value}`}
/>
)
}
```
### Area Chart Component
Stacked area chart for visualizing volume changes over time with multiple data series.
```tsx
import { AreaChart } from "@/components/ui/area-chart"
export default function TrafficChart() {
const data = [
{ month: "Jan", mobile: 2000, desktop: 3000, tablet: 1000 },
{ month: "Feb", mobile: 2200, desktop: 3200, tablet: 1100 },
{ month: "Mar", mobile: 2800, desktop: 3800, tablet: 1300 },
{ month: "Apr", mobile: 3200, desktop: 4200, tablet: 1500 },
{ month: "May", mobile: 3800, desktop: 4800, tablet: 1800 }
]
return (
)
}
```
### Radar Chart Component
Multi-axis chart for comparing multiple variables across different categories simultaneously.
```tsx
import { RadarChart } from "@/components/ui/radar-chart"
export default function SkillsChart() {
const data = [
{ skill: "JavaScript", score: 85, industry: 75 },
{ skill: "TypeScript", score: 80, industry: 70 },
{ skill: "React", score: 90, industry: 80 },
{ skill: "Node.js", score: 75, industry: 72 },
{ skill: "CSS", score: 88, industry: 78 }
]
return (
)
}
```
### Mixed Chart Component
Combined bar and line chart for displaying multiple data types with different visualization methods.
```tsx
import { MixedChart } from "@/components/ui/mixed-chart"
export default function PerformanceChart() {
const data = [
{ month: "Jan", revenue: 4000, growth: 5.2 },
{ month: "Feb", revenue: 4200, growth: 5.0 },
{ month: "Mar", revenue: 4800, growth: 14.3 },
{ month: "Apr", revenue: 5200, growth: 8.3 },
{ month: "May", revenue: 5800, growth: 11.5 }
]
return (
)
}
```
## Animation Components
### Magnetic Effect Component
Magnetic hover effect that smoothly follows cursor movement for interactive buttons and cards.
```tsx
import { Magnetic } from "@/components/ui/magnetic"
export default function InteractiveButton() {
return (
Hover me
)
}
```
### Animated Cursor Component
Custom animated cursor with interactive effects and particle trails for immersive experiences.
```tsx
import { AnimatedCursor } from "@/components/ui/animated-cursor"
export default function Layout({ children }) {
return (
<>
{children}
)
}
```
### Apple Hello Effect Component
Recreation of Apple's iconic "hello" animation with multi-language text transitions.
```tsx
import { AppleHello } from "@/components/ui/apple-hello"
export default function WelcomeScreen() {
const greetings = [
{ text: "Hello", lang: "en" },
{ text: "Bonjour", lang: "fr" },
{ text: "こんにちは", lang: "ja" },
{ text: "Hola", lang: "es" },
{ text: "你好", lang: "zh" }
]
return (
)
}
```
### Liquid Button Component
Button with fluid liquid animation effect on hover for engaging call-to-action elements.
```tsx
import { LiquidButton } from "@/components/ui/liquid-button"
export default function CTASection() {
return (
console.log("CTA clicked")}
>
Get Started
)
}
```
### Rolling Text Component
Text animation that creates a rolling effect with smooth character transitions.
```tsx
import { RollingText } from "@/components/ui/rolling-text"
export default function AnimatedHeading() {
return (
)
}
```
### Shimmering Text Component
Text with animated shimmer effect for attention-grabbing headings and highlights.
```tsx
import { ShimmeringText } from "@/components/ui/shimmering-text"
export default function Hero() {
return (
)
}
```
## React Hooks
### useBoolean Hook
Enhanced boolean state management with toggle, enable, and disable methods for cleaner component logic.
```tsx
import { useBoolean } from "@/hooks/use-boolean"
export default function TogglePanel() {
const modal = useBoolean(false)
const loading = useBoolean(false)
const handleSubmit = async () => {
loading.setTrue()
try {
await submitForm()
modal.setFalse()
} finally {
loading.setFalse()
}
}
return (
<>
Toggle Modal
{modal.value && (
Status: {loading.value ? "Saving..." : "Ready"}
Submit
)}
)
}
```
### useCounter Hook
Counter hook with increment, decrement, reset, and set functionality for numeric state management.
```tsx
import { useCounter } from "@/hooks/use-counter"
export default function CartCounter() {
const quantity = useCounter(0, { min: 0, max: 99 })
return (
-
{quantity.value}
+
Reset
)
}
```
### useLocalStorage Hook
Persist state in browser localStorage with automatic serialization and deserialization.
```tsx
import { useLocalStorage } from "@/hooks/use-local-storage"
export default function UserPreferences() {
const [theme, setTheme] = useLocalStorage("theme", "light")
const [settings, setSettings] = useLocalStorage("settings", {
notifications: true,
emailUpdates: false
})
return (
setTheme(e.target.value)}>
LightDark setSettings({
...settings,
notifications: e.target.checked
})}
/>
Enable Notifications
)
}
```
### useDebounceValue Hook
Debounce values to prevent excessive updates and API calls during rapid user input.
```tsx
import { useDebounceValue } from "@/hooks/use-debounce-value"
import { useState, useEffect } from "react"
export default function SearchBox() {
const [search, setSearch] = useState("")
const debouncedSearch = useDebounceValue(search, 500)
const [results, setResults] = useState([])
const [apiCalls, setApiCalls] = useState(0)
useEffect(() => {
if (debouncedSearch) {
setApiCalls(prev => prev + 1)
fetch(`/api/search?q=${debouncedSearch}`)
.then(res => res.json())
.then(setResults)
}
}, [debouncedSearch])
return (
setSearch(e.target.value)}
placeholder="Search..."
/>
API calls: {apiCalls}
)
}
```
### useHover Hook
Track hover state on elements with customizable enter and leave delays for tooltip and preview functionality.
```tsx
import { useHover } from "@/hooks/use-hover"
import { useRef } from "react"
export default function ImagePreview() {
const hoverRef = useRef(null)
const isHovering = useHover(hoverRef, {
enterDelay: 200,
leaveDelay: 100
})
return (
![Preview](http://https:%2F%2Fcontext7.com%2Fwebsites%2Fshadcn_io%2Fllms.txt/thumbnail.jpg)
{isHovering && (
![Full size](http://https:%2F%2Fcontext7.com%2Fwebsites%2Fshadcn_io%2Fllms.txt/full-size.jpg)
)}
)
}
```
### useCountdown Hook
Countdown timer with play, pause, reset controls and completion callbacks for time-limited features.
```tsx
import { useCountdown } from "@/hooks/use-countdown"
export default function OTPTimer() {
const countdown = useCountdown({
initialSeconds: 60,
onComplete: () => alert("OTP expired! Request a new code.")
})
return (
{countdown.seconds}s
{!countdown.isRunning ? (
Start
) : (
Pause
)}
Reset
Status: {countdown.isComplete ? "Expired" : countdown.isRunning ? "Active" : "Paused"}
)
}
```
## Installation and Usage
### CLI Installation
Install components directly into your project using the shadcn CLI for instant integration.
```bash
# Initialize shadcn in your project
npx shadcn@latest init
# Add individual components
npx shadcn@latest add terminal
npx shadcn@latest add dock
npx shadcn@latest add credit-card
# Add multiple components at once
npx shadcn@latest add bar-chart line-chart pie-chart
# Add hooks
npx shadcn@latest add use-boolean use-counter use-local-storage
```
### Project Configuration
Configure your project to work with shadcn.io components using TypeScript and Tailwind CSS.
```typescript
// tailwind.config.ts
import type { Config } from "tailwindcss"
const config: Config = {
darkMode: ["class"],
content: [
"./pages/**/*.{ts,tsx}",
"./components/**/*.{ts,tsx}",
"./app/**/*.{ts,tsx}",
],
theme: {
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
},
},
},
plugins: [require("tailwindcss-animate")],
}
export default config
```
## Summary
The shadcn.io component library serves as a comprehensive toolkit for React developers building modern web applications with Next.js and TypeScript. The library's primary use cases include rapid prototyping of user interfaces, building data-rich dashboards with interactive charts, creating engaging user experiences with animations and effects, and implementing common UI patterns without writing boilerplate code. The copy-paste approach gives developers complete ownership of their components, allowing for deep customization while maintaining consistency with shadcn/ui design principles. Components are particularly well-suited for SaaS applications, admin panels, marketing websites, and e-commerce platforms that require professional, accessible UI elements.
Integration patterns center around composability and customization rather than rigid package dependencies. Developers can cherry-pick individual components using the CLI, modify them at the source level to match their design system, and combine them with existing shadcn/ui components for a cohesive interface. The library supports both light and dark themes through CSS variables, integrates seamlessly with Tailwind CSS utility classes, and follows React best practices for performance and accessibility. Custom hooks provide reusable logic patterns that complement the visual components, creating a complete ecosystem for building feature-rich applications. The TypeScript-first approach ensures type safety throughout the development process, while the Recharts integration for data visualization provides powerful charting capabilities without additional configuration overhead.

File diff suppressed because it is too large Load diff

47
.beads/.beads.bak/.gitignore vendored Normal file
View file

@ -0,0 +1,47 @@
# Dolt database (managed by Dolt, not git)
dolt/
dolt-access.lock
# Runtime files
bd.sock
sync-state.json
last-touched
# Local version tracking (prevents upgrade notification spam after git ops)
.local_version
# Worktree redirect file (contains relative path to main repo's .beads/)
# Must not be committed as paths would be wrong in other clones
redirect
# Sync state (local-only, per-machine)
# These files are machine-specific and should not be shared across clones
.sync.lock
.jsonl.lock
sync_base.jsonl
export-state/
# Legacy files (from pre-Dolt versions)
*.db
*.db?*
*.db-journal
*.db-wal
*.db-shm
db.sqlite
bd.db
daemon.lock
daemon.log
daemon-*.log.gz
daemon.pid
beads.base.jsonl
beads.base.meta.json
beads.left.jsonl
beads.left.meta.json
beads.right.jsonl
beads.right.meta.json
# NOTE: Do NOT add negation patterns (e.g., !issues.jsonl) here.
# They would override fork protection in .git/info/exclude, allowing
# contributors to accidentally commit upstream issue databases.
# The JSONL files (issues.jsonl, interactions.jsonl) and config files
# are tracked by git by default since no pattern above ignores them.

View file

@ -0,0 +1,81 @@
# Beads - AI-Native Issue Tracking
Welcome to Beads! This repository uses **Beads** for issue tracking - a modern, AI-native tool designed to live directly in your codebase alongside your code.
## What is Beads?
Beads is issue tracking that lives in your repo, making it perfect for AI coding agents and developers who want their issues close to their code. No web UI required - everything works through the CLI and integrates seamlessly with git.
**Learn more:** [github.com/steveyegge/beads](https://github.com/steveyegge/beads)
## Quick Start
### Essential Commands
```bash
# Create new issues
bd create "Add user authentication"
# View all issues
bd list
# View issue details
bd show <issue-id>
# Update issue status
bd update <issue-id> --status in_progress
bd update <issue-id> --status done
# Sync with git remote
bd sync
```
### Working with Issues
Issues in Beads are:
- **Git-native**: Stored in `.beads/issues.jsonl` and synced like code
- **AI-friendly**: CLI-first design works perfectly with AI coding agents
- **Branch-aware**: Issues can follow your branch workflow
- **Always in sync**: Auto-syncs with your commits
## Why Beads?
✨ **AI-Native Design**
- Built specifically for AI-assisted development workflows
- CLI-first interface works seamlessly with AI coding agents
- No context switching to web UIs
🚀 **Developer Focused**
- Issues live in your repo, right next to your code
- Works offline, syncs when you push
- Fast, lightweight, and stays out of your way
🔧 **Git Integration**
- Automatic sync with git commits
- Branch-aware issue tracking
- Intelligent JSONL merge resolution
## Get Started with Beads
Try Beads in your own projects:
```bash
# Install Beads
curl -sSL https://raw.githubusercontent.com/steveyegge/beads/main/scripts/install.sh | bash
# Initialize in your repo
bd init
# Create your first issue
bd create "Try out Beads"
```
## Learn More
- **Documentation**: [github.com/steveyegge/beads/docs](https://github.com/steveyegge/beads/tree/main/docs)
- **Quick Start Guide**: Run `bd quickstart`
- **Examples**: [github.com/steveyegge/beads/examples](https://github.com/steveyegge/beads/tree/main/examples)
---
*Beads: Issue tracking that moves at the speed of thought* ⚡

View file

@ -0,0 +1 @@
{"version":"0.49.6","timestamp":"2026-02-18T20:27:49.7992545-08:00","commit":"14a50ad"}

View file

@ -0,0 +1,63 @@
# Beads Configuration File
# This file configures default behavior for all bd commands in this repository
# All settings can also be set via environment variables (BD_* prefix)
# or overridden with command-line flags
# Issue prefix for this repository (used by bd init)
# If not set, bd init will auto-detect from directory name
# Example: issue-prefix: "myproject" creates issues like "myproject-1", "myproject-2", etc.
issue-prefix: "bb"
# Use no-db mode: load from JSONL, no SQLite, write back after each command
# When true, bd will use .beads/issues.jsonl as the source of truth
# instead of SQLite database
no-db: true
# Disable daemon for RPC communication (forces direct database access)
# no-daemon: false
# Disable auto-flush of database to JSONL after mutations
# no-auto-flush: false
# Disable auto-import from JSONL when it's newer than database
# no-auto-import: false
# Enable JSON output by default
# json: false
# Default actor for audit trails (overridden by BD_ACTOR or --actor)
# actor: ""
# Path to database (overridden by BEADS_DB or --db)
# db: ""
# Auto-start daemon if not running (can also use BEADS_AUTO_START_DAEMON)
# auto-start-daemon: true
# Debounce interval for auto-flush (can also use BEADS_FLUSH_DEBOUNCE)
# flush-debounce: "5s"
# Git branch for beads commits (bd sync will commit to this branch)
# IMPORTANT: Set this for team projects so all clones use the same sync branch.
# This setting persists across clones (unlike database config which is gitignored).
# Can also use BEADS_SYNC_BRANCH env var for local override.
# If not set, bd sync will require you to run 'bd config set sync.branch <branch>'.
sync-branch: "beads-sync"
# Multi-repo configuration (experimental - bd-307)
# Allows hydrating from multiple repositories and routing writes to the correct JSONL
# repos:
# primary: "." # Primary repo (where this database lives)
# additional: # Additional repos to hydrate from (read-only)
# - ~/beads-planning # Personal planning repo
# - ~/work-planning # Work planning repo
# Integration settings (access with 'bd config get/set')
# These are stored in the database, not in this file:
# - jira.url
# - jira.project
# - linear.url
# - linear.api-key
# - github.org
# - github.repo

View file

@ -0,0 +1,16 @@
LEGACY DATABASE DETECTED!
This database was created before version 0.17.5 and lacks a repository fingerprint.
To continue using this database, you must explicitly set its repository ID:
bd migrate --update-repo-id
This ensures the database is bound to this repository and prevents accidental
database sharing between different repositories.
If this is a fresh clone, run:
rm -rf .beads && bd init
Note: Auto-claiming legacy databases is intentionally disabled to prevent
silent corruption when databases are copied between repositories.

View file

@ -0,0 +1,23 @@
#!/usr/bin/env sh
# bd-shim v1
# bd-hooks-version: 0.53.0
#
# bd (beads) post-checkout hook - thin shim
#
# This shim delegates to 'bd hook post-checkout' which contains
# the actual hook logic. This pattern ensures hook behavior is always
# in sync with the installed bd version - no manual updates needed.
#
# The 'bd hook' command (singular) supports:
# - Guard against frequent firing (only imports if JSONL changed)
# - Per-worktree state tracking
# - Dolt branch-then-merge pattern
# - Hook chaining configuration
# Check if bd is available
if ! command -v bd >/dev/null 2>&1; then
# Silently skip - post-checkout is called frequently
exit 0
fi
exec bd hook post-checkout "$@"

View file

@ -0,0 +1,24 @@
#!/usr/bin/env sh
# bd-shim v1
# bd-hooks-version: 0.53.0
#
# bd (beads) post-merge hook - thin shim
#
# This shim delegates to 'bd hook post-merge' which contains
# the actual hook logic. This pattern ensures hook behavior is always
# in sync with the installed bd version - no manual updates needed.
#
# The 'bd hook' command (singular) supports:
# - Branch-then-merge pattern for Dolt (cell-level conflict resolution)
# - Per-worktree state tracking
# - Hook chaining configuration
# Check if bd is available
if ! command -v bd >/dev/null 2>&1; then
echo "Warning: bd command not found in PATH, skipping post-merge hook" >&2
echo " Install bd: brew install beads" >&2
echo " Or add bd to your PATH" >&2
exit 0
fi
exec bd hook post-merge "$@"

View file

@ -0,0 +1,25 @@
#!/usr/bin/env sh
# bd-shim v2
# bd-hooks-version: 0.53.0
#
# bd (beads) pre-commit hook — thin shim
#
# Delegates to 'bd hook pre-commit' which contains the actual hook logic.
# This pattern ensures hook behavior is always in sync with the installed
# bd version — no manual updates needed.
#
# The 'bd hook' command supports:
# - Per-worktree export state tracking
# - Dolt in-process export (no lock deadlocks)
# - Sync-branch routing
# - Hook chaining configuration
# Check if bd is available
if ! command -v bd >/dev/null 2>&1; then
echo "Warning: bd command not found in PATH, skipping pre-commit hook" >&2
echo " Install bd: brew install beads" >&2
echo " Or add bd to your PATH" >&2
exit 0
fi
exec bd hook pre-commit "$@"

View file

@ -0,0 +1,19 @@
#!/usr/bin/env sh
# bd-shim v1
# bd-hooks-version: 0.53.0
#
# bd (beads) pre-push hook - thin shim
#
# This shim delegates to 'bd hooks run pre-push' which contains
# the actual hook logic. This pattern ensures hook behavior is always
# in sync with the installed bd version - no manual updates needed.
# Check if bd is available
if ! command -v bd >/dev/null 2>&1; then
echo "Warning: bd command not found in PATH, skipping pre-push hook" >&2
echo " Install bd: brew install beads" >&2
echo " Or add bd to your PATH" >&2
exit 0
fi
exec bd hooks run pre-push "$@"

View file

@ -0,0 +1,24 @@
#!/usr/bin/env sh
# bd-shim v1
# bd-hooks-version: 0.48.0
#
# bd (beads) prepare-commit-msg hook - thin shim
#
# This shim delegates to 'bd hooks run prepare-commit-msg' which contains
# the actual hook logic. This pattern ensures hook behavior is always
# in sync with the installed bd version - no manual updates needed.
#
# Arguments:
# $1 = path to the commit message file
# $2 = source of commit message (message, template, merge, squash, commit)
# $3 = commit SHA-1 (if -c, -C, or --amend)
# Check if bd is available
if ! command -v bd >/dev/null 2>&1; then
echo "Warning: bd command not found in PATH, skipping prepare-commit-msg hook" >&2
echo " Install bd: brew install beads" >&2
echo " Or add bd to your PATH" >&2
exit 0
fi
exec bd hooks run prepare-commit-msg "$@"

View file

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,6 @@
{
"database": "dolt",
"jsonl_export": "issues.jsonl",
"backend": "dolt",
"dolt_database": "beads_bb"
}

6
.beads/.gitignore vendored
View file

@ -8,10 +8,8 @@
# Daemon runtime files
daemon.lock
daemon.log
daemon-*.log.gz
daemon.pid
bd.sock
bd.sock.startlock
sync-state.json
last-touched
@ -41,10 +39,6 @@ beads.right.meta.json
sync_base.jsonl
export-state/
# Dolt database (managed by Dolt remotes, not git)
dolt/
dolt-access.lock
# NOTE: Do NOT add negation patterns (e.g., !issues.jsonl) here.
# They would override fork protection in .git/info/exclude, allowing
# contributors to accidentally commit upstream issue databases.

View file

@ -0,0 +1,15 @@
{
"id": "architect",
"name": "System Architect",
"description": "Designs complex system structures and writes detailed implementation plans.",
"systemPrompt": "You are a staff-level software architect focused on high-level system design.",
"capabilities": [
"planning",
"design_docs",
"arch_review"
],
"color": "#3b82f6",
"createdAt": "2026-02-21T03:19:47.072Z",
"updatedAt": "2026-02-21T19:39:45.331Z",
"isBuiltIn": true
}

View file

@ -0,0 +1,15 @@
{
"id": "coder",
"name": "Implementation Engineer",
"description": "Translates plans into precise, type-safe, and tested code.",
"systemPrompt": "You are a senior software engineer focused on execution and clean code.",
"capabilities": [
"coding",
"refactoring",
"testing"
],
"color": "#10b981",
"createdAt": "2026-02-21T19:37:55.262Z",
"updatedAt": "2026-02-21T19:37:55.262Z",
"isBuiltIn": true
}

View file

@ -0,0 +1,14 @@
{
"id": "test-agent",
"name": "Test Agent",
"description": "A test archetype for verification",
"systemPrompt": "You are a test agent.",
"capabilities": [
"testing",
"verification"
],
"color": "#ff6b6b",
"createdAt": "2026-02-21T20:43:00.085Z",
"updatedAt": "2026-02-21T20:43:00.085Z",
"isBuiltIn": false
}

View file

@ -0,0 +1 @@
{"version":"0.49.6","timestamp":"2026-02-21T10:21:43.4424965-08:00","commit":"dfaf523"}

View file

@ -37,12 +37,17 @@
# Debounce interval for auto-flush (can also use BEADS_FLUSH_DEBOUNCE)
# flush-debounce: "5s"
# Export events (audit trail) to .beads/events.jsonl on each flush/sync
# When enabled, new events are appended incrementally using a high-water mark.
# Use 'bd export --events' to trigger manually regardless of this setting.
# events-export: false
# Git branch for beads commits (bd sync will commit to this branch)
# IMPORTANT: Set this for team projects so all clones use the same sync branch.
# This setting persists across clones (unlike database config which is gitignored).
# Can also use BEADS_SYNC_BRANCH env var for local override.
# If not set, bd sync will require you to run 'bd config set sync.branch <branch>'.
sync-branch: "beads-sync"
# sync-branch: "beads-sync"
# Multi-repo configuration (experimental - bd-307)
# Allows hydrating from multiple repositories and routing writes to the correct JSONL
@ -60,5 +65,3 @@ sync-branch: "beads-sync"
# - linear.api-key
# - github.org
# - github.repo
dolt.database: "beads_bb"

View file

@ -1,6 +1,4 @@
{
"database": "dolt",
"jsonl_export": "issues.jsonl",
"backend": "dolt",
"dolt_database": "beads_bb"
"database": "beads.db",
"jsonl_export": "issues.jsonl"
}

View file

@ -0,0 +1,18 @@
{
"id": "standard-app",
"name": "Standard Application Swarm",
"description": "A balanced team of an Architect and two Coders for standard feature development.",
"team": [
{
"archetypeId": "architect",
"count": 1
},
{
"archetypeId": "coder",
"count": 2
}
],
"createdAt": "2026-02-21T03:22:04.089Z",
"updatedAt": "2026-02-21T19:39:45.443Z",
"isBuiltIn": true
}

View file

@ -0,0 +1,14 @@
{
"id": "test",
"name": "test",
"description": "test",
"team": [
{
"archetypeId": "architect",
"count": 1
}
],
"createdAt": "2026-02-21T19:37:35.674Z",
"updatedAt": "2026-02-21T19:37:35.674Z",
"isBuiltIn": false
}

View file

@ -1 +0,0 @@
/mnt/c/Users/Zenchant/codex/beadboard/.agents/skills/shadcn-ui/

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,306 @@
### shadcn/ui Chart Component - Installation
Source: https://ui.shadcn.com/docs/components/chart
The chart component in shadcn/ui is built on Recharts, providing direct access to all Recharts capabilities with consistent theming.
```bash
npx shadcn@latest add chart
```
--------------------------------
### shadcn/ui Chart Component - Basic Usage
Source: https://ui.shadcn.com/docs/components/chart
The ChartContainer wraps your Recharts component and accepts a config prop for theming. Requires `min-h-[value]` for responsiveness.
```tsx
import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from "recharts"
import { ChartContainer, ChartTooltipContent } from "@/components/ui/chart"
const chartConfig = {
desktop: {
label: "Desktop",
color: "var(--chart-1)",
},
mobile: {
label: "Mobile",
color: "var(--chart-2)",
},
} satisfies import("@/components/ui/chart").ChartConfig
const chartData = [
{ month: "January", desktop: 186, mobile: 80 },
{ month: "February", desktop: 305, mobile: 200 },
{ month: "March", desktop: 237, mobile: 120 },
]
export function BarChartDemo() {
return (
<ChartContainer config={chartConfig} className="min-h-[200px] w-full">
<BarChart data={chartData}>
<CartesianGrid vertical={false} />
<XAxis dataKey="month" tickLine={false} axisLine={false} />
<Bar dataKey="desktop" fill="var(--color-desktop)" radius={4} />
<Bar dataKey="mobile" fill="var(--color-mobile)" radius={4} />
<ChartTooltip content={<ChartTooltipContent />} />
</BarChart>
</ChartContainer>
)
}
```
--------------------------------
### shadcn/ui Chart Component - ChartConfig with Custom Colors
Source: https://ui.shadcn.com/docs/components/chart
You can define custom colors directly in the configuration using hex values or CSS variables.
```tsx
const chartConfig = {
desktop: {
label: "Desktop",
color: "#2563eb",
theme: {
light: "#2563eb",
dark: "#60a5fa",
},
},
mobile: {
label: "Mobile",
color: "var(--chart-2)",
},
} satisfies import("@/components/ui/chart").ChartConfig
```
--------------------------------
### shadcn/ui Chart Component - CSS Variables
Source: https://ui.shadcn.com/docs/components/chart
Add chart color variables to your globals.css for consistent theming.
```css
:root {
/* Chart colors */
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.546 0.198 38.228);
--chart-4: oklch(0.596 0.151 343.253);
--chart-5: oklch(0.546 0.158 49.157);
}
.dark {
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.698 0.141 24.311);
--chart-4: oklch(0.676 0.172 171.196);
--chart-5: oklch(0.578 0.192 302.85);
}
```
--------------------------------
### shadcn/ui Chart Component - Line Chart Example
Source: https://ui.shadcn.com/docs/components/chart
Creating a line chart with shadcn/ui charts component.
```tsx
import { Line, LineChart, CartesianGrid, XAxis, YAxis } from "recharts"
import { ChartContainer, ChartTooltipContent } from "@/components/ui/chart"
const chartConfig = {
price: {
label: "Price",
color: "var(--chart-1)",
},
} satisfies import("@/components/ui/chart").ChartConfig
const chartData = [
{ month: "January", price: 186 },
{ month: "February", price: 305 },
{ month: "March", price: 237 },
{ month: "April", price: 203 },
{ month: "May", price: 276 },
]
export function LineChartDemo() {
return (
<ChartContainer config={chartConfig} className="min-h-[200px]">
<LineChart data={chartData}>
<CartesianGrid vertical={false} />
<XAxis dataKey="month" tickLine={false} axisLine={false} />
<YAxis tickLine={false} axisLine={false} tickFormatter={(value) => `$${value}`} />
<Line
dataKey="price"
stroke="var(--color-price)"
strokeWidth={2}
dot={false}
/>
<ChartTooltip content={<ChartTooltipContent />} />
</LineChart>
</ChartContainer>
)
}
```
--------------------------------
### shadcn/ui Chart Component - Area Chart Example
Source: https://ui.shadcn.com/docs/components/chart
Creating an area chart with gradient fill and legend.
```tsx
import { Area, AreaChart, XAxis, YAxis } from "recharts"
import {
ChartContainer,
ChartLegend,
ChartLegendContent,
ChartTooltipContent,
} from "@/components/ui/chart"
const chartConfig = {
desktop: { label: "Desktop", color: "var(--chart-1)" },
mobile: { label: "Mobile", color: "var(--chart-2)" },
} satisfies import("@/components/ui/chart").ChartConfig
export function AreaChartDemo() {
return (
<ChartContainer config={chartConfig} className="min-h-[200px]">
<AreaChart data={chartData}>
<XAxis dataKey="month" tickLine={false} axisLine={false} />
<YAxis tickLine={false} axisLine={false} />
<Area
dataKey="desktop"
fill="var(--color-desktop)"
stroke="var(--color-desktop)"
fillOpacity={0.3}
/>
<Area
dataKey="mobile"
fill="var(--color-mobile)"
stroke="var(--color-mobile)"
fillOpacity={0.3}
/>
<ChartTooltip content={<ChartTooltipContent />} />
<ChartLegend content={<ChartLegendContent />} />
</AreaChart>
</ChartContainer>
)
}
```
--------------------------------
### shadcn/ui Chart Component - Pie Chart Example
Source: https://ui.shadcn.com/docs/components/chart
Creating a pie/donut chart with shadcn/ui.
```tsx
import { Pie, PieChart } from "recharts"
import {
ChartContainer,
ChartLegend,
ChartLegendContent,
ChartTooltipContent,
} from "@/components/ui/chart"
const chartConfig = {
chrome: { label: "Chrome", color: "var(--chart-1)" },
safari: { label: "Safari", color: "var(--chart-2)" },
firefox: { label: "Firefox", color: "var(--chart-3)" },
} satisfies import("@/components/ui/chart").ChartConfig
const pieData = [
{ browser: "Chrome", visitors: 275, fill: "var(--color-chrome)" },
{ browser: "Safari", visitors: 200, fill: "var(--color-safari)" },
{ browser: "Firefox", visitors: 187, fill: "var(--color-firefox)" },
]
export function PieChartDemo() {
return (
<ChartContainer config={chartConfig} className="min-h-[200px]">
<PieChart>
<Pie
data={pieData}
dataKey="visitors"
nameKey="browser"
cx="50%"
cy="50%"
outerRadius={80}
/>
<ChartTooltip content={<ChartTooltipContent />} />
<ChartLegend content={<ChartLegendContent />} />
</PieChart>
</ChartContainer>
)
}
```
--------------------------------
### shadcn/ui ChartTooltipContent Props
Source: https://ui.shadcn.com/docs/components/chart
The ChartTooltipContent component accepts these props for customizing tooltip behavior.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `labelKey` | string | "label" | Key for tooltip label |
| `nameKey` | string | "name" | Key for tooltip name |
| `indicator` | "dot" \| "line" \| "dashed" | "dot" | Indicator style |
| `hideLabel` | boolean | false | Hide label |
| `hideIndicator` | boolean | false | Hide indicator |
--------------------------------
### shadcn/ui Chart Component - Accessibility
Source: https://ui.shadcn.com/docs/components/chart
Enable keyboard navigation and screen reader support by adding the accessibilityLayer prop.
```tsx
<BarChart accessibilityLayer data={chartData}>
<CartesianGrid vertical={false} />
<XAxis dataKey="month" />
<Bar dataKey="desktop" fill="var(--color-desktop)" />
<ChartTooltip content={<ChartTooltipContent />} />
</BarChart>
```
This adds:
- Keyboard arrow key navigation
- ARIA labels for chart elements
- Screen reader announcements for data values
--------------------------------
### shadcn/ui Chart Component - Recharts Dependencies
Source: https://ui.shadcn.com/docs/components/chart
The chart component requires the following Recharts dependencies to be installed.
```bash
pnpm add recharts
npm install recharts
yarn add recharts
```
Recharts provides the following chart types:
- Area, Bar, Line, Pie, Composed
- Radar, RadialBar, Scatter
- Funnel, Treemap

View file

@ -0,0 +1,145 @@
# shadcn/ui Learning Guide
This guide helps you learn shadcn/ui from basics to advanced patterns.
## Learning Path
### 1. Understanding the Philosophy
shadcn/ui is different from traditional component libraries:
- **Copy-paste components**: Components are copied into your project, not installed as packages
- **Full customization**: You own the code and can modify it freely
- **Built on Radix UI**: Provides accessibility primitives
- **Styled with Tailwind**: Uses utility classes for consistent styling
### 2. Core Concepts to Master
#### Class Variance Authority (CVA)
Most components use CVA for variant management:
```tsx
const buttonVariants = cva(
"base-classes",
{
variants: {
variant: {
default: "variant-classes",
destructive: "destructive-classes",
},
size: {
default: "size-classes",
sm: "small-classes",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
```
#### cn Utility Function
The `cn` function combines classes and resolves conflicts:
```tsx
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
```
### 3. Installation Checklist
- [ ] Initialize a new project (Next.js, Vite, or Remix)
- [ ] Install Tailwind CSS
- [ ] Run `npx shadcn@latest init`
- [ ] Configure CSS variables
- [ ] Install first component: `npx shadcn@latest add button`
### 4. Essential Components to Learn First
1. **Button** - Learn variants and sizes
2. **Input** - Form inputs with labels
3. **Card** - Container components
4. **Form** - Form handling with React Hook Form
5. **Dialog** - Modal windows
6. **Select** - Dropdown selections
7. **Toast** - Notifications
### 5. Common Patterns
#### Form Pattern
Every form follows this structure:
```tsx
1. Define Zod schema
2. Create form with useForm
3. Wrap with Form component
4. Add FormField for each input
5. Handle submission
```
#### Component Customization Pattern
To customize a component:
1. Copy component to your project
2. Modify the variants
3. Add new props if needed
4. Update types
### 6. Best Practices
- Always use TypeScript
- Follow the existing component structure
- Use semantic HTML when possible
- Test with screen readers for accessibility
- Keep components small and focused
### 7. Advanced Topics
- Creating custom components from scratch
- Building complex forms with validation
- Implementing dark mode
- Optimizing for performance
- Testing components
## Practice Exercises
### Exercise 1: Basic Setup
1. Create a new Next.js project
2. Set up shadcn/ui
3. Install and customize a Button component
4. Add a new variant "gradient"
### Exercise 2: Form Building
1. Create a contact form with:
- Name input (required)
- Email input (email validation)
- Message textarea (min length)
- Submit button with loading state
### Exercise 3: Component Combination
1. Build a settings page using:
- Card for layout
- Sheet for mobile menu
- Select for dropdowns
- Switch for toggles
- Toast for notifications
### Exercise 4: Custom Component
1. Create a custom Badge component
2. Support variants: default, secondary, destructive, outline
3. Support sizes: sm, default, lg
4. Add icon support
## Resources
- [Official Documentation](https://ui.shadcn.com)
- [GitHub Repository](https://github.com/shadcn/ui)
- [Examples Gallery](https://ui.shadcn.com/examples)
- [Radix UI Primitives](https://www.radix-ui.com/primitives)
- [Tailwind CSS Documentation](https://tailwindcss.com/docs)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,586 @@
# shadcn.io Component Library
shadcn.io is a comprehensive React UI component library built on shadcn/ui principles, providing developers with production-ready, composable components for modern web applications. The library serves as a centralized resource for React developers who need high-quality UI components with TypeScript support, ranging from basic interactive elements to advanced AI-powered integrations. Unlike traditional component libraries that require package installations, shadcn.io components are designed to be copied directly into your project, giving you full control and customization capabilities.
The library encompasses four major categories: composable UI components (terminal, dock, credit cards, QR codes, color pickers), chart components built with Recharts, animation components with Tailwind CSS integration, and custom React hooks for state management and lifecycle operations. Each component follows best practices for accessibility, performance, and developer experience, with comprehensive TypeScript definitions and Next.js compatibility. The platform emphasizes flexibility and customization, allowing developers to modify components at the source level rather than being constrained by package APIs.
## Core Components
### Terminal Component
Interactive terminal emulator with typing animations and command execution simulation for developer-focused interfaces.
```tsx
import { Terminal } from "@/components/ui/terminal"
export default function DemoTerminal() {
return (
npm install @repo/terminalInstalling dependencies...npm start
)
}
```
### Dock Component
macOS-style application dock with smooth magnification effects on hover, perfect for navigation menus.
```tsx
import { Dock, DockIcon } from "@/components/ui/dock"
import { Home, Settings, User, Mail } from "lucide-react"
export default function AppDock() {
return (
)
}
```
### Credit Card Component
Interactive 3D credit card component with flip animations for payment forms and card displays.
```tsx
import { CreditCard } from "@/components/ui/credit-card"
import { useState } from "react"
export default function PaymentForm() {
const [cardData, setCardData] = useState({
number: "4532 1234 5678 9010",
holder: "JOHN DOE",
expiry: "12/28",
cvv: "123"
})
return (
console.log("Card flipped:", flipped)}
/>
)
}
```
### Image Zoom Component
Zoomable image component with smooth modal transitions for image galleries and product displays.
```tsx
import { ImageZoom } from "@/components/ui/image-zoom"
export default function ProductGallery() {
return (
)
}
```
### QR Code Component
Generate and display customizable QR codes with styling options for links, contact information, and authentication.
```tsx
import { QRCode } from "@/components/ui/qr-code"
export default function ShareDialog() {
const shareUrl = "https://shadcn.io"
return (
Scan to visit shadcn.io
)
}
```
### Color Picker Component
Advanced color selection component supporting multiple color formats (HEX, RGB, HSL) with preview.
```tsx
import { ColorPicker } from "@/components/ui/color-picker"
import { useState } from "react"
export default function ThemeCustomizer() {
const [color, setColor] = useState("#3b82f6")
return (
Selected: {color}
)
}
```
## Chart Components
### Bar Chart Component
Clean bar chart component for data comparison and categorical analysis using Recharts.
```tsx
import { BarChart } from "@/components/ui/bar-chart"
export default function SalesChart() {
const data = [
{ month: "Jan", sales: 4000, revenue: 2400 },
{ month: "Feb", sales: 3000, revenue: 1398 },
{ month: "Mar", sales: 2000, revenue: 9800 },
{ month: "Apr", sales: 2780, revenue: 3908 },
{ month: "May", sales: 1890, revenue: 4800 },
{ month: "Jun", sales: 2390, revenue: 3800 }
]
return (
`$${value.toLocaleString()}`}
yAxisWidth={60}
/>
)
}
```
### Line Chart Component
Smooth line chart for visualizing trends and time-series data with multiple data series support.
```tsx
import { LineChart } from "@/components/ui/line-chart"
export default function MetricsChart() {
const data = [
{ date: "2024-01", users: 1200, sessions: 3400 },
{ date: "2024-02", users: 1800, sessions: 4200 },
{ date: "2024-03", users: 2400, sessions: 5800 },
{ date: "2024-04", users: 3100, sessions: 7200 },
{ date: "2024-05", users: 3800, sessions: 8900 }
]
return (
)
}
```
### Pie Chart Component
Donut chart component for displaying proportional data and percentage distributions.
```tsx
import { PieChart } from "@/components/ui/pie-chart"
export default function MarketShareChart() {
const data = [
{ name: "Product A", value: 400, fill: "#3b82f6" },
{ name: "Product B", value: 300, fill: "#10b981" },
{ name: "Product C", value: 300, fill: "#f59e0b" },
{ name: "Product D", value: 200, fill: "#ef4444" }
]
return (
`${entry.name}: ${entry.value}`}
/>
)
}
```
### Area Chart Component
Stacked area chart for visualizing volume changes over time with multiple data series.
```tsx
import { AreaChart } from "@/components/ui/area-chart"
export default function TrafficChart() {
const data = [
{ month: "Jan", mobile: 2000, desktop: 3000, tablet: 1000 },
{ month: "Feb", mobile: 2200, desktop: 3200, tablet: 1100 },
{ month: "Mar", mobile: 2800, desktop: 3800, tablet: 1300 },
{ month: "Apr", mobile: 3200, desktop: 4200, tablet: 1500 },
{ month: "May", mobile: 3800, desktop: 4800, tablet: 1800 }
]
return (
)
}
```
### Radar Chart Component
Multi-axis chart for comparing multiple variables across different categories simultaneously.
```tsx
import { RadarChart } from "@/components/ui/radar-chart"
export default function SkillsChart() {
const data = [
{ skill: "JavaScript", score: 85, industry: 75 },
{ skill: "TypeScript", score: 80, industry: 70 },
{ skill: "React", score: 90, industry: 80 },
{ skill: "Node.js", score: 75, industry: 72 },
{ skill: "CSS", score: 88, industry: 78 }
]
return (
)
}
```
### Mixed Chart Component
Combined bar and line chart for displaying multiple data types with different visualization methods.
```tsx
import { MixedChart } from "@/components/ui/mixed-chart"
export default function PerformanceChart() {
const data = [
{ month: "Jan", revenue: 4000, growth: 5.2 },
{ month: "Feb", revenue: 4200, growth: 5.0 },
{ month: "Mar", revenue: 4800, growth: 14.3 },
{ month: "Apr", revenue: 5200, growth: 8.3 },
{ month: "May", revenue: 5800, growth: 11.5 }
]
return (
)
}
```
## Animation Components
### Magnetic Effect Component
Magnetic hover effect that smoothly follows cursor movement for interactive buttons and cards.
```tsx
import { Magnetic } from "@/components/ui/magnetic"
export default function InteractiveButton() {
return (
Hover me
)
}
```
### Animated Cursor Component
Custom animated cursor with interactive effects and particle trails for immersive experiences.
```tsx
import { AnimatedCursor } from "@/components/ui/animated-cursor"
export default function Layout({ children }) {
return (
<>
{children}
)
}
```
### Apple Hello Effect Component
Recreation of Apple's iconic "hello" animation with multi-language text transitions.
```tsx
import { AppleHello } from "@/components/ui/apple-hello"
export default function WelcomeScreen() {
const greetings = [
{ text: "Hello", lang: "en" },
{ text: "Bonjour", lang: "fr" },
{ text: "こんにちは", lang: "ja" },
{ text: "Hola", lang: "es" },
{ text: "你好", lang: "zh" }
]
return (
)
}
```
### Liquid Button Component
Button with fluid liquid animation effect on hover for engaging call-to-action elements.
```tsx
import { LiquidButton } from "@/components/ui/liquid-button"
export default function CTASection() {
return (
console.log("CTA clicked")}
>
Get Started
)
}
```
### Rolling Text Component
Text animation that creates a rolling effect with smooth character transitions.
```tsx
import { RollingText } from "@/components/ui/rolling-text"
export default function AnimatedHeading() {
return (
)
}
```
### Shimmering Text Component
Text with animated shimmer effect for attention-grabbing headings and highlights.
```tsx
import { ShimmeringText } from "@/components/ui/shimmering-text"
export default function Hero() {
return (
)
}
```
## React Hooks
### useBoolean Hook
Enhanced boolean state management with toggle, enable, and disable methods for cleaner component logic.
```tsx
import { useBoolean } from "@/hooks/use-boolean"
export default function TogglePanel() {
const modal = useBoolean(false)
const loading = useBoolean(false)
const handleSubmit = async () => {
loading.setTrue()
try {
await submitForm()
modal.setFalse()
} finally {
loading.setFalse()
}
}
return (
<>
Toggle Modal
{modal.value && (
Status: {loading.value ? "Saving..." : "Ready"}
Submit
)}
)
}
```
### useCounter Hook
Counter hook with increment, decrement, reset, and set functionality for numeric state management.
```tsx
import { useCounter } from "@/hooks/use-counter"
export default function CartCounter() {
const quantity = useCounter(0, { min: 0, max: 99 })
return (
-
{quantity.value}
+
Reset
)
}
```
### useLocalStorage Hook
Persist state in browser localStorage with automatic serialization and deserialization.
```tsx
import { useLocalStorage } from "@/hooks/use-local-storage"
export default function UserPreferences() {
const [theme, setTheme] = useLocalStorage("theme", "light")
const [settings, setSettings] = useLocalStorage("settings", {
notifications: true,
emailUpdates: false
})
return (
setTheme(e.target.value)}>
LightDark setSettings({
...settings,
notifications: e.target.checked
})}
/>
Enable Notifications
)
}
```
### useDebounceValue Hook
Debounce values to prevent excessive updates and API calls during rapid user input.
```tsx
import { useDebounceValue } from "@/hooks/use-debounce-value"
import { useState, useEffect } from "react"
export default function SearchBox() {
const [search, setSearch] = useState("")
const debouncedSearch = useDebounceValue(search, 500)
const [results, setResults] = useState([])
const [apiCalls, setApiCalls] = useState(0)
useEffect(() => {
if (debouncedSearch) {
setApiCalls(prev => prev + 1)
fetch(`/api/search?q=${debouncedSearch}`)
.then(res => res.json())
.then(setResults)
}
}, [debouncedSearch])
return (
setSearch(e.target.value)}
placeholder="Search..."
/>
API calls: {apiCalls}
)
}
```
### useHover Hook
Track hover state on elements with customizable enter and leave delays for tooltip and preview functionality.
```tsx
import { useHover } from "@/hooks/use-hover"
import { useRef } from "react"
export default function ImagePreview() {
const hoverRef = useRef(null)
const isHovering = useHover(hoverRef, {
enterDelay: 200,
leaveDelay: 100
})
return (
![Preview](http://https:%2F%2Fcontext7.com%2Fwebsites%2Fshadcn_io%2Fllms.txt/thumbnail.jpg)
{isHovering && (
![Full size](http://https:%2F%2Fcontext7.com%2Fwebsites%2Fshadcn_io%2Fllms.txt/full-size.jpg)
)}
)
}
```
### useCountdown Hook
Countdown timer with play, pause, reset controls and completion callbacks for time-limited features.
```tsx
import { useCountdown } from "@/hooks/use-countdown"
export default function OTPTimer() {
const countdown = useCountdown({
initialSeconds: 60,
onComplete: () => alert("OTP expired! Request a new code.")
})
return (
{countdown.seconds}s
{!countdown.isRunning ? (
Start
) : (
Pause
)}
Reset
Status: {countdown.isComplete ? "Expired" : countdown.isRunning ? "Active" : "Paused"}
)
}
```
## Installation and Usage
### CLI Installation
Install components directly into your project using the shadcn CLI for instant integration.
```bash
# Initialize shadcn in your project
npx shadcn@latest init
# Add individual components
npx shadcn@latest add terminal
npx shadcn@latest add dock
npx shadcn@latest add credit-card
# Add multiple components at once
npx shadcn@latest add bar-chart line-chart pie-chart
# Add hooks
npx shadcn@latest add use-boolean use-counter use-local-storage
```
### Project Configuration
Configure your project to work with shadcn.io components using TypeScript and Tailwind CSS.
```typescript
// tailwind.config.ts
import type { Config } from "tailwindcss"
const config: Config = {
darkMode: ["class"],
content: [
"./pages/**/*.{ts,tsx}",
"./components/**/*.{ts,tsx}",
"./app/**/*.{ts,tsx}",
],
theme: {
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
},
},
},
plugins: [require("tailwindcss-animate")],
}
export default config
```
## Summary
The shadcn.io component library serves as a comprehensive toolkit for React developers building modern web applications with Next.js and TypeScript. The library's primary use cases include rapid prototyping of user interfaces, building data-rich dashboards with interactive charts, creating engaging user experiences with animations and effects, and implementing common UI patterns without writing boilerplate code. The copy-paste approach gives developers complete ownership of their components, allowing for deep customization while maintaining consistency with shadcn/ui design principles. Components are particularly well-suited for SaaS applications, admin panels, marketing websites, and e-commerce platforms that require professional, accessible UI elements.
Integration patterns center around composability and customization rather than rigid package dependencies. Developers can cherry-pick individual components using the CLI, modify them at the source level to match their design system, and combine them with existing shadcn/ui components for a cohesive interface. The library supports both light and dark themes through CSS variables, integrates seamlessly with Tailwind CSS utility classes, and follows React best practices for performance and accessibility. Custom hooks provide reusable logic patterns that complement the visual components, creating a complete ecosystem for building feature-rich applications. The TypeScript-first approach ensures type safety throughout the development process, while the Recharts integration for data visualization provides powerful charting capabilities without additional configuration overhead.

File diff suppressed because it is too large Load diff

View file

@ -1 +0,0 @@
/mnt/c/Users/Zenchant/codex/beadboard/.agents/skills/shadcn-ui/

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,306 @@
### shadcn/ui Chart Component - Installation
Source: https://ui.shadcn.com/docs/components/chart
The chart component in shadcn/ui is built on Recharts, providing direct access to all Recharts capabilities with consistent theming.
```bash
npx shadcn@latest add chart
```
--------------------------------
### shadcn/ui Chart Component - Basic Usage
Source: https://ui.shadcn.com/docs/components/chart
The ChartContainer wraps your Recharts component and accepts a config prop for theming. Requires `min-h-[value]` for responsiveness.
```tsx
import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from "recharts"
import { ChartContainer, ChartTooltipContent } from "@/components/ui/chart"
const chartConfig = {
desktop: {
label: "Desktop",
color: "var(--chart-1)",
},
mobile: {
label: "Mobile",
color: "var(--chart-2)",
},
} satisfies import("@/components/ui/chart").ChartConfig
const chartData = [
{ month: "January", desktop: 186, mobile: 80 },
{ month: "February", desktop: 305, mobile: 200 },
{ month: "March", desktop: 237, mobile: 120 },
]
export function BarChartDemo() {
return (
<ChartContainer config={chartConfig} className="min-h-[200px] w-full">
<BarChart data={chartData}>
<CartesianGrid vertical={false} />
<XAxis dataKey="month" tickLine={false} axisLine={false} />
<Bar dataKey="desktop" fill="var(--color-desktop)" radius={4} />
<Bar dataKey="mobile" fill="var(--color-mobile)" radius={4} />
<ChartTooltip content={<ChartTooltipContent />} />
</BarChart>
</ChartContainer>
)
}
```
--------------------------------
### shadcn/ui Chart Component - ChartConfig with Custom Colors
Source: https://ui.shadcn.com/docs/components/chart
You can define custom colors directly in the configuration using hex values or CSS variables.
```tsx
const chartConfig = {
desktop: {
label: "Desktop",
color: "#2563eb",
theme: {
light: "#2563eb",
dark: "#60a5fa",
},
},
mobile: {
label: "Mobile",
color: "var(--chart-2)",
},
} satisfies import("@/components/ui/chart").ChartConfig
```
--------------------------------
### shadcn/ui Chart Component - CSS Variables
Source: https://ui.shadcn.com/docs/components/chart
Add chart color variables to your globals.css for consistent theming.
```css
:root {
/* Chart colors */
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.546 0.198 38.228);
--chart-4: oklch(0.596 0.151 343.253);
--chart-5: oklch(0.546 0.158 49.157);
}
.dark {
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.698 0.141 24.311);
--chart-4: oklch(0.676 0.172 171.196);
--chart-5: oklch(0.578 0.192 302.85);
}
```
--------------------------------
### shadcn/ui Chart Component - Line Chart Example
Source: https://ui.shadcn.com/docs/components/chart
Creating a line chart with shadcn/ui charts component.
```tsx
import { Line, LineChart, CartesianGrid, XAxis, YAxis } from "recharts"
import { ChartContainer, ChartTooltipContent } from "@/components/ui/chart"
const chartConfig = {
price: {
label: "Price",
color: "var(--chart-1)",
},
} satisfies import("@/components/ui/chart").ChartConfig
const chartData = [
{ month: "January", price: 186 },
{ month: "February", price: 305 },
{ month: "March", price: 237 },
{ month: "April", price: 203 },
{ month: "May", price: 276 },
]
export function LineChartDemo() {
return (
<ChartContainer config={chartConfig} className="min-h-[200px]">
<LineChart data={chartData}>
<CartesianGrid vertical={false} />
<XAxis dataKey="month" tickLine={false} axisLine={false} />
<YAxis tickLine={false} axisLine={false} tickFormatter={(value) => `$${value}`} />
<Line
dataKey="price"
stroke="var(--color-price)"
strokeWidth={2}
dot={false}
/>
<ChartTooltip content={<ChartTooltipContent />} />
</LineChart>
</ChartContainer>
)
}
```
--------------------------------
### shadcn/ui Chart Component - Area Chart Example
Source: https://ui.shadcn.com/docs/components/chart
Creating an area chart with gradient fill and legend.
```tsx
import { Area, AreaChart, XAxis, YAxis } from "recharts"
import {
ChartContainer,
ChartLegend,
ChartLegendContent,
ChartTooltipContent,
} from "@/components/ui/chart"
const chartConfig = {
desktop: { label: "Desktop", color: "var(--chart-1)" },
mobile: { label: "Mobile", color: "var(--chart-2)" },
} satisfies import("@/components/ui/chart").ChartConfig
export function AreaChartDemo() {
return (
<ChartContainer config={chartConfig} className="min-h-[200px]">
<AreaChart data={chartData}>
<XAxis dataKey="month" tickLine={false} axisLine={false} />
<YAxis tickLine={false} axisLine={false} />
<Area
dataKey="desktop"
fill="var(--color-desktop)"
stroke="var(--color-desktop)"
fillOpacity={0.3}
/>
<Area
dataKey="mobile"
fill="var(--color-mobile)"
stroke="var(--color-mobile)"
fillOpacity={0.3}
/>
<ChartTooltip content={<ChartTooltipContent />} />
<ChartLegend content={<ChartLegendContent />} />
</AreaChart>
</ChartContainer>
)
}
```
--------------------------------
### shadcn/ui Chart Component - Pie Chart Example
Source: https://ui.shadcn.com/docs/components/chart
Creating a pie/donut chart with shadcn/ui.
```tsx
import { Pie, PieChart } from "recharts"
import {
ChartContainer,
ChartLegend,
ChartLegendContent,
ChartTooltipContent,
} from "@/components/ui/chart"
const chartConfig = {
chrome: { label: "Chrome", color: "var(--chart-1)" },
safari: { label: "Safari", color: "var(--chart-2)" },
firefox: { label: "Firefox", color: "var(--chart-3)" },
} satisfies import("@/components/ui/chart").ChartConfig
const pieData = [
{ browser: "Chrome", visitors: 275, fill: "var(--color-chrome)" },
{ browser: "Safari", visitors: 200, fill: "var(--color-safari)" },
{ browser: "Firefox", visitors: 187, fill: "var(--color-firefox)" },
]
export function PieChartDemo() {
return (
<ChartContainer config={chartConfig} className="min-h-[200px]">
<PieChart>
<Pie
data={pieData}
dataKey="visitors"
nameKey="browser"
cx="50%"
cy="50%"
outerRadius={80}
/>
<ChartTooltip content={<ChartTooltipContent />} />
<ChartLegend content={<ChartLegendContent />} />
</PieChart>
</ChartContainer>
)
}
```
--------------------------------
### shadcn/ui ChartTooltipContent Props
Source: https://ui.shadcn.com/docs/components/chart
The ChartTooltipContent component accepts these props for customizing tooltip behavior.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `labelKey` | string | "label" | Key for tooltip label |
| `nameKey` | string | "name" | Key for tooltip name |
| `indicator` | "dot" \| "line" \| "dashed" | "dot" | Indicator style |
| `hideLabel` | boolean | false | Hide label |
| `hideIndicator` | boolean | false | Hide indicator |
--------------------------------
### shadcn/ui Chart Component - Accessibility
Source: https://ui.shadcn.com/docs/components/chart
Enable keyboard navigation and screen reader support by adding the accessibilityLayer prop.
```tsx
<BarChart accessibilityLayer data={chartData}>
<CartesianGrid vertical={false} />
<XAxis dataKey="month" />
<Bar dataKey="desktop" fill="var(--color-desktop)" />
<ChartTooltip content={<ChartTooltipContent />} />
</BarChart>
```
This adds:
- Keyboard arrow key navigation
- ARIA labels for chart elements
- Screen reader announcements for data values
--------------------------------
### shadcn/ui Chart Component - Recharts Dependencies
Source: https://ui.shadcn.com/docs/components/chart
The chart component requires the following Recharts dependencies to be installed.
```bash
pnpm add recharts
npm install recharts
yarn add recharts
```
Recharts provides the following chart types:
- Area, Bar, Line, Pie, Composed
- Radar, RadialBar, Scatter
- Funnel, Treemap

View file

@ -0,0 +1,145 @@
# shadcn/ui Learning Guide
This guide helps you learn shadcn/ui from basics to advanced patterns.
## Learning Path
### 1. Understanding the Philosophy
shadcn/ui is different from traditional component libraries:
- **Copy-paste components**: Components are copied into your project, not installed as packages
- **Full customization**: You own the code and can modify it freely
- **Built on Radix UI**: Provides accessibility primitives
- **Styled with Tailwind**: Uses utility classes for consistent styling
### 2. Core Concepts to Master
#### Class Variance Authority (CVA)
Most components use CVA for variant management:
```tsx
const buttonVariants = cva(
"base-classes",
{
variants: {
variant: {
default: "variant-classes",
destructive: "destructive-classes",
},
size: {
default: "size-classes",
sm: "small-classes",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
```
#### cn Utility Function
The `cn` function combines classes and resolves conflicts:
```tsx
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
```
### 3. Installation Checklist
- [ ] Initialize a new project (Next.js, Vite, or Remix)
- [ ] Install Tailwind CSS
- [ ] Run `npx shadcn@latest init`
- [ ] Configure CSS variables
- [ ] Install first component: `npx shadcn@latest add button`
### 4. Essential Components to Learn First
1. **Button** - Learn variants and sizes
2. **Input** - Form inputs with labels
3. **Card** - Container components
4. **Form** - Form handling with React Hook Form
5. **Dialog** - Modal windows
6. **Select** - Dropdown selections
7. **Toast** - Notifications
### 5. Common Patterns
#### Form Pattern
Every form follows this structure:
```tsx
1. Define Zod schema
2. Create form with useForm
3. Wrap with Form component
4. Add FormField for each input
5. Handle submission
```
#### Component Customization Pattern
To customize a component:
1. Copy component to your project
2. Modify the variants
3. Add new props if needed
4. Update types
### 6. Best Practices
- Always use TypeScript
- Follow the existing component structure
- Use semantic HTML when possible
- Test with screen readers for accessibility
- Keep components small and focused
### 7. Advanced Topics
- Creating custom components from scratch
- Building complex forms with validation
- Implementing dark mode
- Optimizing for performance
- Testing components
## Practice Exercises
### Exercise 1: Basic Setup
1. Create a new Next.js project
2. Set up shadcn/ui
3. Install and customize a Button component
4. Add a new variant "gradient"
### Exercise 2: Form Building
1. Create a contact form with:
- Name input (required)
- Email input (email validation)
- Message textarea (min length)
- Submit button with loading state
### Exercise 3: Component Combination
1. Build a settings page using:
- Card for layout
- Sheet for mobile menu
- Select for dropdowns
- Switch for toggles
- Toast for notifications
### Exercise 4: Custom Component
1. Create a custom Badge component
2. Support variants: default, secondary, destructive, outline
3. Support sizes: sm, default, lg
4. Add icon support
## Resources
- [Official Documentation](https://ui.shadcn.com)
- [GitHub Repository](https://github.com/shadcn/ui)
- [Examples Gallery](https://ui.shadcn.com/examples)
- [Radix UI Primitives](https://www.radix-ui.com/primitives)
- [Tailwind CSS Documentation](https://tailwindcss.com/docs)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,586 @@
# shadcn.io Component Library
shadcn.io is a comprehensive React UI component library built on shadcn/ui principles, providing developers with production-ready, composable components for modern web applications. The library serves as a centralized resource for React developers who need high-quality UI components with TypeScript support, ranging from basic interactive elements to advanced AI-powered integrations. Unlike traditional component libraries that require package installations, shadcn.io components are designed to be copied directly into your project, giving you full control and customization capabilities.
The library encompasses four major categories: composable UI components (terminal, dock, credit cards, QR codes, color pickers), chart components built with Recharts, animation components with Tailwind CSS integration, and custom React hooks for state management and lifecycle operations. Each component follows best practices for accessibility, performance, and developer experience, with comprehensive TypeScript definitions and Next.js compatibility. The platform emphasizes flexibility and customization, allowing developers to modify components at the source level rather than being constrained by package APIs.
## Core Components
### Terminal Component
Interactive terminal emulator with typing animations and command execution simulation for developer-focused interfaces.
```tsx
import { Terminal } from "@/components/ui/terminal"
export default function DemoTerminal() {
return (
npm install @repo/terminalInstalling dependencies...npm start
)
}
```
### Dock Component
macOS-style application dock with smooth magnification effects on hover, perfect for navigation menus.
```tsx
import { Dock, DockIcon } from "@/components/ui/dock"
import { Home, Settings, User, Mail } from "lucide-react"
export default function AppDock() {
return (
)
}
```
### Credit Card Component
Interactive 3D credit card component with flip animations for payment forms and card displays.
```tsx
import { CreditCard } from "@/components/ui/credit-card"
import { useState } from "react"
export default function PaymentForm() {
const [cardData, setCardData] = useState({
number: "4532 1234 5678 9010",
holder: "JOHN DOE",
expiry: "12/28",
cvv: "123"
})
return (
console.log("Card flipped:", flipped)}
/>
)
}
```
### Image Zoom Component
Zoomable image component with smooth modal transitions for image galleries and product displays.
```tsx
import { ImageZoom } from "@/components/ui/image-zoom"
export default function ProductGallery() {
return (
)
}
```
### QR Code Component
Generate and display customizable QR codes with styling options for links, contact information, and authentication.
```tsx
import { QRCode } from "@/components/ui/qr-code"
export default function ShareDialog() {
const shareUrl = "https://shadcn.io"
return (
Scan to visit shadcn.io
)
}
```
### Color Picker Component
Advanced color selection component supporting multiple color formats (HEX, RGB, HSL) with preview.
```tsx
import { ColorPicker } from "@/components/ui/color-picker"
import { useState } from "react"
export default function ThemeCustomizer() {
const [color, setColor] = useState("#3b82f6")
return (
Selected: {color}
)
}
```
## Chart Components
### Bar Chart Component
Clean bar chart component for data comparison and categorical analysis using Recharts.
```tsx
import { BarChart } from "@/components/ui/bar-chart"
export default function SalesChart() {
const data = [
{ month: "Jan", sales: 4000, revenue: 2400 },
{ month: "Feb", sales: 3000, revenue: 1398 },
{ month: "Mar", sales: 2000, revenue: 9800 },
{ month: "Apr", sales: 2780, revenue: 3908 },
{ month: "May", sales: 1890, revenue: 4800 },
{ month: "Jun", sales: 2390, revenue: 3800 }
]
return (
`$${value.toLocaleString()}`}
yAxisWidth={60}
/>
)
}
```
### Line Chart Component
Smooth line chart for visualizing trends and time-series data with multiple data series support.
```tsx
import { LineChart } from "@/components/ui/line-chart"
export default function MetricsChart() {
const data = [
{ date: "2024-01", users: 1200, sessions: 3400 },
{ date: "2024-02", users: 1800, sessions: 4200 },
{ date: "2024-03", users: 2400, sessions: 5800 },
{ date: "2024-04", users: 3100, sessions: 7200 },
{ date: "2024-05", users: 3800, sessions: 8900 }
]
return (
)
}
```
### Pie Chart Component
Donut chart component for displaying proportional data and percentage distributions.
```tsx
import { PieChart } from "@/components/ui/pie-chart"
export default function MarketShareChart() {
const data = [
{ name: "Product A", value: 400, fill: "#3b82f6" },
{ name: "Product B", value: 300, fill: "#10b981" },
{ name: "Product C", value: 300, fill: "#f59e0b" },
{ name: "Product D", value: 200, fill: "#ef4444" }
]
return (
`${entry.name}: ${entry.value}`}
/>
)
}
```
### Area Chart Component
Stacked area chart for visualizing volume changes over time with multiple data series.
```tsx
import { AreaChart } from "@/components/ui/area-chart"
export default function TrafficChart() {
const data = [
{ month: "Jan", mobile: 2000, desktop: 3000, tablet: 1000 },
{ month: "Feb", mobile: 2200, desktop: 3200, tablet: 1100 },
{ month: "Mar", mobile: 2800, desktop: 3800, tablet: 1300 },
{ month: "Apr", mobile: 3200, desktop: 4200, tablet: 1500 },
{ month: "May", mobile: 3800, desktop: 4800, tablet: 1800 }
]
return (
)
}
```
### Radar Chart Component
Multi-axis chart for comparing multiple variables across different categories simultaneously.
```tsx
import { RadarChart } from "@/components/ui/radar-chart"
export default function SkillsChart() {
const data = [
{ skill: "JavaScript", score: 85, industry: 75 },
{ skill: "TypeScript", score: 80, industry: 70 },
{ skill: "React", score: 90, industry: 80 },
{ skill: "Node.js", score: 75, industry: 72 },
{ skill: "CSS", score: 88, industry: 78 }
]
return (
)
}
```
### Mixed Chart Component
Combined bar and line chart for displaying multiple data types with different visualization methods.
```tsx
import { MixedChart } from "@/components/ui/mixed-chart"
export default function PerformanceChart() {
const data = [
{ month: "Jan", revenue: 4000, growth: 5.2 },
{ month: "Feb", revenue: 4200, growth: 5.0 },
{ month: "Mar", revenue: 4800, growth: 14.3 },
{ month: "Apr", revenue: 5200, growth: 8.3 },
{ month: "May", revenue: 5800, growth: 11.5 }
]
return (
)
}
```
## Animation Components
### Magnetic Effect Component
Magnetic hover effect that smoothly follows cursor movement for interactive buttons and cards.
```tsx
import { Magnetic } from "@/components/ui/magnetic"
export default function InteractiveButton() {
return (
Hover me
)
}
```
### Animated Cursor Component
Custom animated cursor with interactive effects and particle trails for immersive experiences.
```tsx
import { AnimatedCursor } from "@/components/ui/animated-cursor"
export default function Layout({ children }) {
return (
<>
{children}
)
}
```
### Apple Hello Effect Component
Recreation of Apple's iconic "hello" animation with multi-language text transitions.
```tsx
import { AppleHello } from "@/components/ui/apple-hello"
export default function WelcomeScreen() {
const greetings = [
{ text: "Hello", lang: "en" },
{ text: "Bonjour", lang: "fr" },
{ text: "こんにちは", lang: "ja" },
{ text: "Hola", lang: "es" },
{ text: "你好", lang: "zh" }
]
return (
)
}
```
### Liquid Button Component
Button with fluid liquid animation effect on hover for engaging call-to-action elements.
```tsx
import { LiquidButton } from "@/components/ui/liquid-button"
export default function CTASection() {
return (
console.log("CTA clicked")}
>
Get Started
)
}
```
### Rolling Text Component
Text animation that creates a rolling effect with smooth character transitions.
```tsx
import { RollingText } from "@/components/ui/rolling-text"
export default function AnimatedHeading() {
return (
)
}
```
### Shimmering Text Component
Text with animated shimmer effect for attention-grabbing headings and highlights.
```tsx
import { ShimmeringText } from "@/components/ui/shimmering-text"
export default function Hero() {
return (
)
}
```
## React Hooks
### useBoolean Hook
Enhanced boolean state management with toggle, enable, and disable methods for cleaner component logic.
```tsx
import { useBoolean } from "@/hooks/use-boolean"
export default function TogglePanel() {
const modal = useBoolean(false)
const loading = useBoolean(false)
const handleSubmit = async () => {
loading.setTrue()
try {
await submitForm()
modal.setFalse()
} finally {
loading.setFalse()
}
}
return (
<>
Toggle Modal
{modal.value && (
Status: {loading.value ? "Saving..." : "Ready"}
Submit
)}
)
}
```
### useCounter Hook
Counter hook with increment, decrement, reset, and set functionality for numeric state management.
```tsx
import { useCounter } from "@/hooks/use-counter"
export default function CartCounter() {
const quantity = useCounter(0, { min: 0, max: 99 })
return (
-
{quantity.value}
+
Reset
)
}
```
### useLocalStorage Hook
Persist state in browser localStorage with automatic serialization and deserialization.
```tsx
import { useLocalStorage } from "@/hooks/use-local-storage"
export default function UserPreferences() {
const [theme, setTheme] = useLocalStorage("theme", "light")
const [settings, setSettings] = useLocalStorage("settings", {
notifications: true,
emailUpdates: false
})
return (
setTheme(e.target.value)}>
LightDark setSettings({
...settings,
notifications: e.target.checked
})}
/>
Enable Notifications
)
}
```
### useDebounceValue Hook
Debounce values to prevent excessive updates and API calls during rapid user input.
```tsx
import { useDebounceValue } from "@/hooks/use-debounce-value"
import { useState, useEffect } from "react"
export default function SearchBox() {
const [search, setSearch] = useState("")
const debouncedSearch = useDebounceValue(search, 500)
const [results, setResults] = useState([])
const [apiCalls, setApiCalls] = useState(0)
useEffect(() => {
if (debouncedSearch) {
setApiCalls(prev => prev + 1)
fetch(`/api/search?q=${debouncedSearch}`)
.then(res => res.json())
.then(setResults)
}
}, [debouncedSearch])
return (
setSearch(e.target.value)}
placeholder="Search..."
/>
API calls: {apiCalls}
)
}
```
### useHover Hook
Track hover state on elements with customizable enter and leave delays for tooltip and preview functionality.
```tsx
import { useHover } from "@/hooks/use-hover"
import { useRef } from "react"
export default function ImagePreview() {
const hoverRef = useRef(null)
const isHovering = useHover(hoverRef, {
enterDelay: 200,
leaveDelay: 100
})
return (
![Preview](http://https:%2F%2Fcontext7.com%2Fwebsites%2Fshadcn_io%2Fllms.txt/thumbnail.jpg)
{isHovering && (
![Full size](http://https:%2F%2Fcontext7.com%2Fwebsites%2Fshadcn_io%2Fllms.txt/full-size.jpg)
)}
)
}
```
### useCountdown Hook
Countdown timer with play, pause, reset controls and completion callbacks for time-limited features.
```tsx
import { useCountdown } from "@/hooks/use-countdown"
export default function OTPTimer() {
const countdown = useCountdown({
initialSeconds: 60,
onComplete: () => alert("OTP expired! Request a new code.")
})
return (
{countdown.seconds}s
{!countdown.isRunning ? (
Start
) : (
Pause
)}
Reset
Status: {countdown.isComplete ? "Expired" : countdown.isRunning ? "Active" : "Paused"}
)
}
```
## Installation and Usage
### CLI Installation
Install components directly into your project using the shadcn CLI for instant integration.
```bash
# Initialize shadcn in your project
npx shadcn@latest init
# Add individual components
npx shadcn@latest add terminal
npx shadcn@latest add dock
npx shadcn@latest add credit-card
# Add multiple components at once
npx shadcn@latest add bar-chart line-chart pie-chart
# Add hooks
npx shadcn@latest add use-boolean use-counter use-local-storage
```
### Project Configuration
Configure your project to work with shadcn.io components using TypeScript and Tailwind CSS.
```typescript
// tailwind.config.ts
import type { Config } from "tailwindcss"
const config: Config = {
darkMode: ["class"],
content: [
"./pages/**/*.{ts,tsx}",
"./components/**/*.{ts,tsx}",
"./app/**/*.{ts,tsx}",
],
theme: {
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
},
},
},
plugins: [require("tailwindcss-animate")],
}
export default config
```
## Summary
The shadcn.io component library serves as a comprehensive toolkit for React developers building modern web applications with Next.js and TypeScript. The library's primary use cases include rapid prototyping of user interfaces, building data-rich dashboards with interactive charts, creating engaging user experiences with animations and effects, and implementing common UI patterns without writing boilerplate code. The copy-paste approach gives developers complete ownership of their components, allowing for deep customization while maintaining consistency with shadcn/ui design principles. Components are particularly well-suited for SaaS applications, admin panels, marketing websites, and e-commerce platforms that require professional, accessible UI elements.
Integration patterns center around composability and customization rather than rigid package dependencies. Developers can cherry-pick individual components using the CLI, modify them at the source level to match their design system, and combine them with existing shadcn/ui components for a cohesive interface. The library supports both light and dark themes through CSS variables, integrates seamlessly with Tailwind CSS utility classes, and follows React best practices for performance and accessibility. Custom hooks provide reusable logic patterns that complement the visual components, creating a complete ecosystem for building feature-rich applications. The TypeScript-first approach ensures type safety throughout the development process, while the Recharts integration for data visualization provides powerful charting capabilities without additional configuration overhead.

File diff suppressed because it is too large Load diff

View file

@ -1 +0,0 @@
/mnt/c/Users/Zenchant/codex/beadboard/.agents/skills/shadcn-ui/

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,306 @@
### shadcn/ui Chart Component - Installation
Source: https://ui.shadcn.com/docs/components/chart
The chart component in shadcn/ui is built on Recharts, providing direct access to all Recharts capabilities with consistent theming.
```bash
npx shadcn@latest add chart
```
--------------------------------
### shadcn/ui Chart Component - Basic Usage
Source: https://ui.shadcn.com/docs/components/chart
The ChartContainer wraps your Recharts component and accepts a config prop for theming. Requires `min-h-[value]` for responsiveness.
```tsx
import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from "recharts"
import { ChartContainer, ChartTooltipContent } from "@/components/ui/chart"
const chartConfig = {
desktop: {
label: "Desktop",
color: "var(--chart-1)",
},
mobile: {
label: "Mobile",
color: "var(--chart-2)",
},
} satisfies import("@/components/ui/chart").ChartConfig
const chartData = [
{ month: "January", desktop: 186, mobile: 80 },
{ month: "February", desktop: 305, mobile: 200 },
{ month: "March", desktop: 237, mobile: 120 },
]
export function BarChartDemo() {
return (
<ChartContainer config={chartConfig} className="min-h-[200px] w-full">
<BarChart data={chartData}>
<CartesianGrid vertical={false} />
<XAxis dataKey="month" tickLine={false} axisLine={false} />
<Bar dataKey="desktop" fill="var(--color-desktop)" radius={4} />
<Bar dataKey="mobile" fill="var(--color-mobile)" radius={4} />
<ChartTooltip content={<ChartTooltipContent />} />
</BarChart>
</ChartContainer>
)
}
```
--------------------------------
### shadcn/ui Chart Component - ChartConfig with Custom Colors
Source: https://ui.shadcn.com/docs/components/chart
You can define custom colors directly in the configuration using hex values or CSS variables.
```tsx
const chartConfig = {
desktop: {
label: "Desktop",
color: "#2563eb",
theme: {
light: "#2563eb",
dark: "#60a5fa",
},
},
mobile: {
label: "Mobile",
color: "var(--chart-2)",
},
} satisfies import("@/components/ui/chart").ChartConfig
```
--------------------------------
### shadcn/ui Chart Component - CSS Variables
Source: https://ui.shadcn.com/docs/components/chart
Add chart color variables to your globals.css for consistent theming.
```css
:root {
/* Chart colors */
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.546 0.198 38.228);
--chart-4: oklch(0.596 0.151 343.253);
--chart-5: oklch(0.546 0.158 49.157);
}
.dark {
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.698 0.141 24.311);
--chart-4: oklch(0.676 0.172 171.196);
--chart-5: oklch(0.578 0.192 302.85);
}
```
--------------------------------
### shadcn/ui Chart Component - Line Chart Example
Source: https://ui.shadcn.com/docs/components/chart
Creating a line chart with shadcn/ui charts component.
```tsx
import { Line, LineChart, CartesianGrid, XAxis, YAxis } from "recharts"
import { ChartContainer, ChartTooltipContent } from "@/components/ui/chart"
const chartConfig = {
price: {
label: "Price",
color: "var(--chart-1)",
},
} satisfies import("@/components/ui/chart").ChartConfig
const chartData = [
{ month: "January", price: 186 },
{ month: "February", price: 305 },
{ month: "March", price: 237 },
{ month: "April", price: 203 },
{ month: "May", price: 276 },
]
export function LineChartDemo() {
return (
<ChartContainer config={chartConfig} className="min-h-[200px]">
<LineChart data={chartData}>
<CartesianGrid vertical={false} />
<XAxis dataKey="month" tickLine={false} axisLine={false} />
<YAxis tickLine={false} axisLine={false} tickFormatter={(value) => `$${value}`} />
<Line
dataKey="price"
stroke="var(--color-price)"
strokeWidth={2}
dot={false}
/>
<ChartTooltip content={<ChartTooltipContent />} />
</LineChart>
</ChartContainer>
)
}
```
--------------------------------
### shadcn/ui Chart Component - Area Chart Example
Source: https://ui.shadcn.com/docs/components/chart
Creating an area chart with gradient fill and legend.
```tsx
import { Area, AreaChart, XAxis, YAxis } from "recharts"
import {
ChartContainer,
ChartLegend,
ChartLegendContent,
ChartTooltipContent,
} from "@/components/ui/chart"
const chartConfig = {
desktop: { label: "Desktop", color: "var(--chart-1)" },
mobile: { label: "Mobile", color: "var(--chart-2)" },
} satisfies import("@/components/ui/chart").ChartConfig
export function AreaChartDemo() {
return (
<ChartContainer config={chartConfig} className="min-h-[200px]">
<AreaChart data={chartData}>
<XAxis dataKey="month" tickLine={false} axisLine={false} />
<YAxis tickLine={false} axisLine={false} />
<Area
dataKey="desktop"
fill="var(--color-desktop)"
stroke="var(--color-desktop)"
fillOpacity={0.3}
/>
<Area
dataKey="mobile"
fill="var(--color-mobile)"
stroke="var(--color-mobile)"
fillOpacity={0.3}
/>
<ChartTooltip content={<ChartTooltipContent />} />
<ChartLegend content={<ChartLegendContent />} />
</AreaChart>
</ChartContainer>
)
}
```
--------------------------------
### shadcn/ui Chart Component - Pie Chart Example
Source: https://ui.shadcn.com/docs/components/chart
Creating a pie/donut chart with shadcn/ui.
```tsx
import { Pie, PieChart } from "recharts"
import {
ChartContainer,
ChartLegend,
ChartLegendContent,
ChartTooltipContent,
} from "@/components/ui/chart"
const chartConfig = {
chrome: { label: "Chrome", color: "var(--chart-1)" },
safari: { label: "Safari", color: "var(--chart-2)" },
firefox: { label: "Firefox", color: "var(--chart-3)" },
} satisfies import("@/components/ui/chart").ChartConfig
const pieData = [
{ browser: "Chrome", visitors: 275, fill: "var(--color-chrome)" },
{ browser: "Safari", visitors: 200, fill: "var(--color-safari)" },
{ browser: "Firefox", visitors: 187, fill: "var(--color-firefox)" },
]
export function PieChartDemo() {
return (
<ChartContainer config={chartConfig} className="min-h-[200px]">
<PieChart>
<Pie
data={pieData}
dataKey="visitors"
nameKey="browser"
cx="50%"
cy="50%"
outerRadius={80}
/>
<ChartTooltip content={<ChartTooltipContent />} />
<ChartLegend content={<ChartLegendContent />} />
</PieChart>
</ChartContainer>
)
}
```
--------------------------------
### shadcn/ui ChartTooltipContent Props
Source: https://ui.shadcn.com/docs/components/chart
The ChartTooltipContent component accepts these props for customizing tooltip behavior.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `labelKey` | string | "label" | Key for tooltip label |
| `nameKey` | string | "name" | Key for tooltip name |
| `indicator` | "dot" \| "line" \| "dashed" | "dot" | Indicator style |
| `hideLabel` | boolean | false | Hide label |
| `hideIndicator` | boolean | false | Hide indicator |
--------------------------------
### shadcn/ui Chart Component - Accessibility
Source: https://ui.shadcn.com/docs/components/chart
Enable keyboard navigation and screen reader support by adding the accessibilityLayer prop.
```tsx
<BarChart accessibilityLayer data={chartData}>
<CartesianGrid vertical={false} />
<XAxis dataKey="month" />
<Bar dataKey="desktop" fill="var(--color-desktop)" />
<ChartTooltip content={<ChartTooltipContent />} />
</BarChart>
```
This adds:
- Keyboard arrow key navigation
- ARIA labels for chart elements
- Screen reader announcements for data values
--------------------------------
### shadcn/ui Chart Component - Recharts Dependencies
Source: https://ui.shadcn.com/docs/components/chart
The chart component requires the following Recharts dependencies to be installed.
```bash
pnpm add recharts
npm install recharts
yarn add recharts
```
Recharts provides the following chart types:
- Area, Bar, Line, Pie, Composed
- Radar, RadialBar, Scatter
- Funnel, Treemap

View file

@ -0,0 +1,145 @@
# shadcn/ui Learning Guide
This guide helps you learn shadcn/ui from basics to advanced patterns.
## Learning Path
### 1. Understanding the Philosophy
shadcn/ui is different from traditional component libraries:
- **Copy-paste components**: Components are copied into your project, not installed as packages
- **Full customization**: You own the code and can modify it freely
- **Built on Radix UI**: Provides accessibility primitives
- **Styled with Tailwind**: Uses utility classes for consistent styling
### 2. Core Concepts to Master
#### Class Variance Authority (CVA)
Most components use CVA for variant management:
```tsx
const buttonVariants = cva(
"base-classes",
{
variants: {
variant: {
default: "variant-classes",
destructive: "destructive-classes",
},
size: {
default: "size-classes",
sm: "small-classes",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
```
#### cn Utility Function
The `cn` function combines classes and resolves conflicts:
```tsx
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
```
### 3. Installation Checklist
- [ ] Initialize a new project (Next.js, Vite, or Remix)
- [ ] Install Tailwind CSS
- [ ] Run `npx shadcn@latest init`
- [ ] Configure CSS variables
- [ ] Install first component: `npx shadcn@latest add button`
### 4. Essential Components to Learn First
1. **Button** - Learn variants and sizes
2. **Input** - Form inputs with labels
3. **Card** - Container components
4. **Form** - Form handling with React Hook Form
5. **Dialog** - Modal windows
6. **Select** - Dropdown selections
7. **Toast** - Notifications
### 5. Common Patterns
#### Form Pattern
Every form follows this structure:
```tsx
1. Define Zod schema
2. Create form with useForm
3. Wrap with Form component
4. Add FormField for each input
5. Handle submission
```
#### Component Customization Pattern
To customize a component:
1. Copy component to your project
2. Modify the variants
3. Add new props if needed
4. Update types
### 6. Best Practices
- Always use TypeScript
- Follow the existing component structure
- Use semantic HTML when possible
- Test with screen readers for accessibility
- Keep components small and focused
### 7. Advanced Topics
- Creating custom components from scratch
- Building complex forms with validation
- Implementing dark mode
- Optimizing for performance
- Testing components
## Practice Exercises
### Exercise 1: Basic Setup
1. Create a new Next.js project
2. Set up shadcn/ui
3. Install and customize a Button component
4. Add a new variant "gradient"
### Exercise 2: Form Building
1. Create a contact form with:
- Name input (required)
- Email input (email validation)
- Message textarea (min length)
- Submit button with loading state
### Exercise 3: Component Combination
1. Build a settings page using:
- Card for layout
- Sheet for mobile menu
- Select for dropdowns
- Switch for toggles
- Toast for notifications
### Exercise 4: Custom Component
1. Create a custom Badge component
2. Support variants: default, secondary, destructive, outline
3. Support sizes: sm, default, lg
4. Add icon support
## Resources
- [Official Documentation](https://ui.shadcn.com)
- [GitHub Repository](https://github.com/shadcn/ui)
- [Examples Gallery](https://ui.shadcn.com/examples)
- [Radix UI Primitives](https://www.radix-ui.com/primitives)
- [Tailwind CSS Documentation](https://tailwindcss.com/docs)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,586 @@
# shadcn.io Component Library
shadcn.io is a comprehensive React UI component library built on shadcn/ui principles, providing developers with production-ready, composable components for modern web applications. The library serves as a centralized resource for React developers who need high-quality UI components with TypeScript support, ranging from basic interactive elements to advanced AI-powered integrations. Unlike traditional component libraries that require package installations, shadcn.io components are designed to be copied directly into your project, giving you full control and customization capabilities.
The library encompasses four major categories: composable UI components (terminal, dock, credit cards, QR codes, color pickers), chart components built with Recharts, animation components with Tailwind CSS integration, and custom React hooks for state management and lifecycle operations. Each component follows best practices for accessibility, performance, and developer experience, with comprehensive TypeScript definitions and Next.js compatibility. The platform emphasizes flexibility and customization, allowing developers to modify components at the source level rather than being constrained by package APIs.
## Core Components
### Terminal Component
Interactive terminal emulator with typing animations and command execution simulation for developer-focused interfaces.
```tsx
import { Terminal } from "@/components/ui/terminal"
export default function DemoTerminal() {
return (
npm install @repo/terminalInstalling dependencies...npm start
)
}
```
### Dock Component
macOS-style application dock with smooth magnification effects on hover, perfect for navigation menus.
```tsx
import { Dock, DockIcon } from "@/components/ui/dock"
import { Home, Settings, User, Mail } from "lucide-react"
export default function AppDock() {
return (
)
}
```
### Credit Card Component
Interactive 3D credit card component with flip animations for payment forms and card displays.
```tsx
import { CreditCard } from "@/components/ui/credit-card"
import { useState } from "react"
export default function PaymentForm() {
const [cardData, setCardData] = useState({
number: "4532 1234 5678 9010",
holder: "JOHN DOE",
expiry: "12/28",
cvv: "123"
})
return (
console.log("Card flipped:", flipped)}
/>
)
}
```
### Image Zoom Component
Zoomable image component with smooth modal transitions for image galleries and product displays.
```tsx
import { ImageZoom } from "@/components/ui/image-zoom"
export default function ProductGallery() {
return (
)
}
```
### QR Code Component
Generate and display customizable QR codes with styling options for links, contact information, and authentication.
```tsx
import { QRCode } from "@/components/ui/qr-code"
export default function ShareDialog() {
const shareUrl = "https://shadcn.io"
return (
Scan to visit shadcn.io
)
}
```
### Color Picker Component
Advanced color selection component supporting multiple color formats (HEX, RGB, HSL) with preview.
```tsx
import { ColorPicker } from "@/components/ui/color-picker"
import { useState } from "react"
export default function ThemeCustomizer() {
const [color, setColor] = useState("#3b82f6")
return (
Selected: {color}
)
}
```
## Chart Components
### Bar Chart Component
Clean bar chart component for data comparison and categorical analysis using Recharts.
```tsx
import { BarChart } from "@/components/ui/bar-chart"
export default function SalesChart() {
const data = [
{ month: "Jan", sales: 4000, revenue: 2400 },
{ month: "Feb", sales: 3000, revenue: 1398 },
{ month: "Mar", sales: 2000, revenue: 9800 },
{ month: "Apr", sales: 2780, revenue: 3908 },
{ month: "May", sales: 1890, revenue: 4800 },
{ month: "Jun", sales: 2390, revenue: 3800 }
]
return (
`$${value.toLocaleString()}`}
yAxisWidth={60}
/>
)
}
```
### Line Chart Component
Smooth line chart for visualizing trends and time-series data with multiple data series support.
```tsx
import { LineChart } from "@/components/ui/line-chart"
export default function MetricsChart() {
const data = [
{ date: "2024-01", users: 1200, sessions: 3400 },
{ date: "2024-02", users: 1800, sessions: 4200 },
{ date: "2024-03", users: 2400, sessions: 5800 },
{ date: "2024-04", users: 3100, sessions: 7200 },
{ date: "2024-05", users: 3800, sessions: 8900 }
]
return (
)
}
```
### Pie Chart Component
Donut chart component for displaying proportional data and percentage distributions.
```tsx
import { PieChart } from "@/components/ui/pie-chart"
export default function MarketShareChart() {
const data = [
{ name: "Product A", value: 400, fill: "#3b82f6" },
{ name: "Product B", value: 300, fill: "#10b981" },
{ name: "Product C", value: 300, fill: "#f59e0b" },
{ name: "Product D", value: 200, fill: "#ef4444" }
]
return (
`${entry.name}: ${entry.value}`}
/>
)
}
```
### Area Chart Component
Stacked area chart for visualizing volume changes over time with multiple data series.
```tsx
import { AreaChart } from "@/components/ui/area-chart"
export default function TrafficChart() {
const data = [
{ month: "Jan", mobile: 2000, desktop: 3000, tablet: 1000 },
{ month: "Feb", mobile: 2200, desktop: 3200, tablet: 1100 },
{ month: "Mar", mobile: 2800, desktop: 3800, tablet: 1300 },
{ month: "Apr", mobile: 3200, desktop: 4200, tablet: 1500 },
{ month: "May", mobile: 3800, desktop: 4800, tablet: 1800 }
]
return (
)
}
```
### Radar Chart Component
Multi-axis chart for comparing multiple variables across different categories simultaneously.
```tsx
import { RadarChart } from "@/components/ui/radar-chart"
export default function SkillsChart() {
const data = [
{ skill: "JavaScript", score: 85, industry: 75 },
{ skill: "TypeScript", score: 80, industry: 70 },
{ skill: "React", score: 90, industry: 80 },
{ skill: "Node.js", score: 75, industry: 72 },
{ skill: "CSS", score: 88, industry: 78 }
]
return (
)
}
```
### Mixed Chart Component
Combined bar and line chart for displaying multiple data types with different visualization methods.
```tsx
import { MixedChart } from "@/components/ui/mixed-chart"
export default function PerformanceChart() {
const data = [
{ month: "Jan", revenue: 4000, growth: 5.2 },
{ month: "Feb", revenue: 4200, growth: 5.0 },
{ month: "Mar", revenue: 4800, growth: 14.3 },
{ month: "Apr", revenue: 5200, growth: 8.3 },
{ month: "May", revenue: 5800, growth: 11.5 }
]
return (
)
}
```
## Animation Components
### Magnetic Effect Component
Magnetic hover effect that smoothly follows cursor movement for interactive buttons and cards.
```tsx
import { Magnetic } from "@/components/ui/magnetic"
export default function InteractiveButton() {
return (
Hover me
)
}
```
### Animated Cursor Component
Custom animated cursor with interactive effects and particle trails for immersive experiences.
```tsx
import { AnimatedCursor } from "@/components/ui/animated-cursor"
export default function Layout({ children }) {
return (
<>
{children}
)
}
```
### Apple Hello Effect Component
Recreation of Apple's iconic "hello" animation with multi-language text transitions.
```tsx
import { AppleHello } from "@/components/ui/apple-hello"
export default function WelcomeScreen() {
const greetings = [
{ text: "Hello", lang: "en" },
{ text: "Bonjour", lang: "fr" },
{ text: "こんにちは", lang: "ja" },
{ text: "Hola", lang: "es" },
{ text: "你好", lang: "zh" }
]
return (
)
}
```
### Liquid Button Component
Button with fluid liquid animation effect on hover for engaging call-to-action elements.
```tsx
import { LiquidButton } from "@/components/ui/liquid-button"
export default function CTASection() {
return (
console.log("CTA clicked")}
>
Get Started
)
}
```
### Rolling Text Component
Text animation that creates a rolling effect with smooth character transitions.
```tsx
import { RollingText } from "@/components/ui/rolling-text"
export default function AnimatedHeading() {
return (
)
}
```
### Shimmering Text Component
Text with animated shimmer effect for attention-grabbing headings and highlights.
```tsx
import { ShimmeringText } from "@/components/ui/shimmering-text"
export default function Hero() {
return (
)
}
```
## React Hooks
### useBoolean Hook
Enhanced boolean state management with toggle, enable, and disable methods for cleaner component logic.
```tsx
import { useBoolean } from "@/hooks/use-boolean"
export default function TogglePanel() {
const modal = useBoolean(false)
const loading = useBoolean(false)
const handleSubmit = async () => {
loading.setTrue()
try {
await submitForm()
modal.setFalse()
} finally {
loading.setFalse()
}
}
return (
<>
Toggle Modal
{modal.value && (
Status: {loading.value ? "Saving..." : "Ready"}
Submit
)}
)
}
```
### useCounter Hook
Counter hook with increment, decrement, reset, and set functionality for numeric state management.
```tsx
import { useCounter } from "@/hooks/use-counter"
export default function CartCounter() {
const quantity = useCounter(0, { min: 0, max: 99 })
return (
-
{quantity.value}
+
Reset
)
}
```
### useLocalStorage Hook
Persist state in browser localStorage with automatic serialization and deserialization.
```tsx
import { useLocalStorage } from "@/hooks/use-local-storage"
export default function UserPreferences() {
const [theme, setTheme] = useLocalStorage("theme", "light")
const [settings, setSettings] = useLocalStorage("settings", {
notifications: true,
emailUpdates: false
})
return (
setTheme(e.target.value)}>
LightDark setSettings({
...settings,
notifications: e.target.checked
})}
/>
Enable Notifications
)
}
```
### useDebounceValue Hook
Debounce values to prevent excessive updates and API calls during rapid user input.
```tsx
import { useDebounceValue } from "@/hooks/use-debounce-value"
import { useState, useEffect } from "react"
export default function SearchBox() {
const [search, setSearch] = useState("")
const debouncedSearch = useDebounceValue(search, 500)
const [results, setResults] = useState([])
const [apiCalls, setApiCalls] = useState(0)
useEffect(() => {
if (debouncedSearch) {
setApiCalls(prev => prev + 1)
fetch(`/api/search?q=${debouncedSearch}`)
.then(res => res.json())
.then(setResults)
}
}, [debouncedSearch])
return (
setSearch(e.target.value)}
placeholder="Search..."
/>
API calls: {apiCalls}
)
}
```
### useHover Hook
Track hover state on elements with customizable enter and leave delays for tooltip and preview functionality.
```tsx
import { useHover } from "@/hooks/use-hover"
import { useRef } from "react"
export default function ImagePreview() {
const hoverRef = useRef(null)
const isHovering = useHover(hoverRef, {
enterDelay: 200,
leaveDelay: 100
})
return (
![Preview](http://https:%2F%2Fcontext7.com%2Fwebsites%2Fshadcn_io%2Fllms.txt/thumbnail.jpg)
{isHovering && (
![Full size](http://https:%2F%2Fcontext7.com%2Fwebsites%2Fshadcn_io%2Fllms.txt/full-size.jpg)
)}
)
}
```
### useCountdown Hook
Countdown timer with play, pause, reset controls and completion callbacks for time-limited features.
```tsx
import { useCountdown } from "@/hooks/use-countdown"
export default function OTPTimer() {
const countdown = useCountdown({
initialSeconds: 60,
onComplete: () => alert("OTP expired! Request a new code.")
})
return (
{countdown.seconds}s
{!countdown.isRunning ? (
Start
) : (
Pause
)}
Reset
Status: {countdown.isComplete ? "Expired" : countdown.isRunning ? "Active" : "Paused"}
)
}
```
## Installation and Usage
### CLI Installation
Install components directly into your project using the shadcn CLI for instant integration.
```bash
# Initialize shadcn in your project
npx shadcn@latest init
# Add individual components
npx shadcn@latest add terminal
npx shadcn@latest add dock
npx shadcn@latest add credit-card
# Add multiple components at once
npx shadcn@latest add bar-chart line-chart pie-chart
# Add hooks
npx shadcn@latest add use-boolean use-counter use-local-storage
```
### Project Configuration
Configure your project to work with shadcn.io components using TypeScript and Tailwind CSS.
```typescript
// tailwind.config.ts
import type { Config } from "tailwindcss"
const config: Config = {
darkMode: ["class"],
content: [
"./pages/**/*.{ts,tsx}",
"./components/**/*.{ts,tsx}",
"./app/**/*.{ts,tsx}",
],
theme: {
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
},
},
},
plugins: [require("tailwindcss-animate")],
}
export default config
```
## Summary
The shadcn.io component library serves as a comprehensive toolkit for React developers building modern web applications with Next.js and TypeScript. The library's primary use cases include rapid prototyping of user interfaces, building data-rich dashboards with interactive charts, creating engaging user experiences with animations and effects, and implementing common UI patterns without writing boilerplate code. The copy-paste approach gives developers complete ownership of their components, allowing for deep customization while maintaining consistency with shadcn/ui design principles. Components are particularly well-suited for SaaS applications, admin panels, marketing websites, and e-commerce platforms that require professional, accessible UI elements.
Integration patterns center around composability and customization rather than rigid package dependencies. Developers can cherry-pick individual components using the CLI, modify them at the source level to match their design system, and combine them with existing shadcn/ui components for a cohesive interface. The library supports both light and dark themes through CSS variables, integrates seamlessly with Tailwind CSS utility classes, and follows React best practices for performance and accessibility. Custom hooks provide reusable logic patterns that complement the visual components, creating a complete ecosystem for building feature-rich applications. The TypeScript-first approach ensures type safety throughout the development process, while the Recharts integration for data visualization provides powerful charting capabilities without additional configuration overhead.

File diff suppressed because it is too large Load diff

50
agent-help.txt Normal file
View file

@ -0,0 +1,50 @@
Manage state on agent beads for ZFC-compliant state reporting.
Agent beads (labeled gt:agent) can self-report their state using these commands.
This enables the Witness and other monitoring systems to track agent health.
States:
idle - Agent is waiting for work
spawning - Agent is starting up
running - Agent is executing (general)
working - Agent is actively working on a task
stuck - Agent is blocked and needs help
done - Agent completed its current work
stopped - Agent has cleanly shut down
dead - Agent died without clean shutdown (set by Witness via timeout)
Examples:
bd agent state gt-emma running # Set emma's state to running
bd agent heartbeat gt-emma # Update emma's last_activity timestamp
bd agent show gt-emma # Show emma's agent details
Usage:
bd agent [command]
Available Commands:
backfill-labels Backfill role_type/rig labels on existing agent beads
heartbeat Update agent last_activity timestamp
show Show agent bead details
state Set agent state
Flags:
-h, --help help for agent
Global Flags:
--actor string Actor name for audit trail (default: $BD_ACTOR, git user.name, $USER)
--allow-stale Allow operations on potentially stale data (skip staleness check)
--db string Database path (default: auto-discover .beads/*.db)
--dolt-auto-commit string Dolt backend: auto-commit after write commands (off|on). Default from config key dolt.auto-commit
--json Output in JSON format
--lock-timeout duration SQLite busy timeout (0 = fail immediately if locked) (default 30s)
--no-auto-flush Disable automatic JSONL sync after CRUD operations
--no-auto-import Disable automatic JSONL import when newer than DB
--no-daemon Force direct storage mode, bypass daemon if running
--no-db Use no-db mode: load from JSONL, no SQLite
--profile Generate CPU profile for performance analysis
-q, --quiet Suppress non-essential output (errors only)
--readonly Read-only mode: block write operations (for worker sandboxes)
--sandbox Sandbox mode: disables daemon and auto-sync
-v, --verbose Enable verbose/debug output
Use "bd agent [command] --help" for more information about a command.

30
epic-help.txt Normal file
View file

@ -0,0 +1,30 @@
Epic management commands
Usage:
bd epic [command]
Available Commands:
close-eligible Close epics where all children are complete
status Show epic completion status
Flags:
-h, --help help for epic
Global Flags:
--actor string Actor name for audit trail (default: $BD_ACTOR, git user.name, $USER)
--allow-stale Allow operations on potentially stale data (skip staleness check)
--db string Database path (default: auto-discover .beads/*.db)
--dolt-auto-commit string Dolt backend: auto-commit after write commands (off|on). Default from config key dolt.auto-commit
--json Output in JSON format
--lock-timeout duration SQLite busy timeout (0 = fail immediately if locked) (default 30s)
--no-auto-flush Disable automatic JSONL sync after CRUD operations
--no-auto-import Disable automatic JSONL import when newer than DB
--no-daemon Force direct storage mode, bypass daemon if running
--no-db Use no-db mode: load from JSONL, no SQLite
--profile Generate CPU profile for performance analysis
-q, --quiet Suppress non-essential output (errors only)
--readonly Read-only mode: block write operations (for worker sandboxes)
--sandbox Sandbox mode: disables daemon and auto-sync
-v, --verbose Enable verbose/debug output
Use "bd epic [command] --help" for more information about a command.

4
epic.txt Normal file
View file

@ -0,0 +1,4 @@
✓ Created issue: beadboard-3xc
Title: Swarm Test: Order Processing System
Priority: P2
Status: open

1
eslint-report.json Normal file

File diff suppressed because one or more lines are too long

136
help.txt Normal file
View file

@ -0,0 +1,136 @@
Issues chained together like beads. A lightweight issue tracker with first-class dependency support.
Usage:
bd [flags]
bd [command]
Maintenance:
rename-prefix Rename the issue prefix for all issues in the database
repair Repair corrupted database by cleaning orphaned references
resolve-conflicts Resolve git merge conflicts in JSONL files
Integrations & Advanced:
Working With Issues:
children List child beads of a parent
close Close one or more issues
comments View or manage comments on an issue
create Create a new issue (or multiple issues from markdown file)
create-form Create a new issue using an interactive form
delete Delete one or more issues and clean up references
edit Edit an issue field in $EDITOR
gate Manage async coordination gates
label Manage issue labels
list List issues
merge-slot Manage merge-slot gates for serialized conflict resolution
move Move an issue to a different rig with dependency remapping
promote Promote a wisp to a permanent bead
q Quick capture: create issue and output only ID
query Query issues using a simple query language
refile Move an issue to a different rig
reopen Reopen one or more closed issues
search Search issues by text query
set-state Set operational state (creates event + updates label)
show Show issue details
state Query the current value of a state dimension
todo Manage TODO items (convenience wrapper for task issues)
update Update one or more issues
Views & Reports:
activity Show real-time molecule state feed
count Count issues matching filters
diff Show changes between two commits or branches (requires Dolt backend)
find-duplicates Find semantically similar issues using text analysis or AI
history Show version history for an issue (requires Dolt backend)
lint Check issues for missing template sections
stale Show stale issues (not updated recently)
status Show issue database overview and statistics
types List valid issue types
Dependencies & Structure:
dep Manage dependencies
duplicate Mark an issue as a duplicate of another
duplicates Find and optionally merge duplicate issues
epic Epic management commands
graph Display issue dependency graph
supersede Mark an issue as superseded by a newer one
swarm Swarm management for structured epics
Sync & Data:
branch List or create branches (requires Dolt backend)
daemon Manage background sync daemon
export Export issues to JSONL or Obsidian format
federation Manage peer-to-peer federation (requires CGO)
import Import issues from JSONL format
merge Git merge driver for beads JSONL files
restore Restore full history of a compacted issue from git
sync Export database to JSONL (sync with git)
vc Version control operations (requires Dolt backend)
Setup & Configuration:
backend Manage storage backend configuration
config Manage configuration settings
hooks Manage git hooks for bd auto-sync
human Show essential commands for human users
info Show database and daemon information
init Initialize bd in the current directory
kv Key-value store commands
onboard Display minimal snippet for AGENTS.md
prime Output AI-optimized workflow context
quickstart Quick start guide for bd
setup Setup integration with AI editors
where Show active beads location
Maintenance:
doctor Check and fix beads installation health (start here)
migrate Database migration commands
preflight Show PR readiness checklist
upgrade Check and manage bd version upgrades
worktree Manage git worktrees for parallel development
Integrations & Advanced:
admin Administrative commands for database maintenance
jira Jira integration commands
linear Linear integration commands
repo Manage multiple repository configuration
Additional Commands:
agent Manage agent bead state
audit Record and label agent interactions (append-only JSONL)
blocked Show blocked issues
completion Generate the autocompletion script for the specified shell
cook Compile a formula into a proto (ephemeral by default)
defer Defer one or more issues for later
formula Manage workflow formulas
gitlab GitLab integration commands
help Help about any command
hook Execute a git hook (called by hook scripts)
mail Delegate to mail provider (e.g., gt mail)
mol Molecule commands (work templates)
orphans Identify orphaned issues (referenced in commits but still open)
ready Show ready work (open, no blockers)
rename Rename an issue ID
ship Publish a capability for cross-project dependencies
slot Manage agent bead slots
undefer Undefer one or more issues (restore to open)
version Print version information
Flags:
--actor string Actor name for audit trail (default: $BD_ACTOR, git user.name, $USER)
--allow-stale Allow operations on potentially stale data (skip staleness check)
--db string Database path (default: auto-discover .beads/*.db)
--dolt-auto-commit string Dolt backend: auto-commit after write commands (off|on). Default from config key dolt.auto-commit
-h, --help help for bd
--json Output in JSON format
--lock-timeout duration SQLite busy timeout (0 = fail immediately if locked) (default 30s)
--no-auto-flush Disable automatic JSONL sync after CRUD operations
--no-auto-import Disable automatic JSONL import when newer than DB
--no-daemon Force direct storage mode, bypass daemon if running
--no-db Use no-db mode: load from JSONL, no SQLite
--profile Generate CPU profile for performance analysis
-q, --quiet Suppress non-essential output (errors only)
--readonly Read-only mode: block write operations (for worker sandboxes)
--sandbox Sandbox mode: disables daemon and auto-sync
-v, --verbose Enable verbose/debug output
-V, --version Print version information
Use "bd [command] --help" for more information about a command.

32
label-help.txt Normal file
View file

@ -0,0 +1,32 @@
Manage issue labels
Usage:
bd label [command]
Available Commands:
add Add a label to one or more issues
list List labels for an issue
list-all List all unique labels in the database
remove Remove a label from one or more issues
Flags:
-h, --help help for label
Global Flags:
--actor string Actor name for audit trail (default: $BD_ACTOR, git user.name, $USER)
--allow-stale Allow operations on potentially stale data (skip staleness check)
--db string Database path (default: auto-discover .beads/*.db)
--dolt-auto-commit string Dolt backend: auto-commit after write commands (off|on). Default from config key dolt.auto-commit
--json Output in JSON format
--lock-timeout duration SQLite busy timeout (0 = fail immediately if locked) (default 30s)
--no-auto-flush Disable automatic JSONL sync after CRUD operations
--no-auto-import Disable automatic JSONL import when newer than DB
--no-daemon Force direct storage mode, bypass daemon if running
--no-db Use no-db mode: load from JSONL, no SQLite
--profile Generate CPU profile for performance analysis
-q, --quiet Suppress non-essential output (errors only)
--readonly Read-only mode: block write operations (for worker sandboxes)
--sandbox Sandbox mode: disables daemon and auto-sync
-v, --verbose Enable verbose/debug output
Use "bd label [command] --help" for more information about a command.

View file

@ -1 +0,0 @@
/mnt/c/Users/Zenchant/codex/beadboard/.agents/skills/shadcn-ui/

1930
skills/shadcn-ui/SKILL.md Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,306 @@
### shadcn/ui Chart Component - Installation
Source: https://ui.shadcn.com/docs/components/chart
The chart component in shadcn/ui is built on Recharts, providing direct access to all Recharts capabilities with consistent theming.
```bash
npx shadcn@latest add chart
```
--------------------------------
### shadcn/ui Chart Component - Basic Usage
Source: https://ui.shadcn.com/docs/components/chart
The ChartContainer wraps your Recharts component and accepts a config prop for theming. Requires `min-h-[value]` for responsiveness.
```tsx
import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from "recharts"
import { ChartContainer, ChartTooltipContent } from "@/components/ui/chart"
const chartConfig = {
desktop: {
label: "Desktop",
color: "var(--chart-1)",
},
mobile: {
label: "Mobile",
color: "var(--chart-2)",
},
} satisfies import("@/components/ui/chart").ChartConfig
const chartData = [
{ month: "January", desktop: 186, mobile: 80 },
{ month: "February", desktop: 305, mobile: 200 },
{ month: "March", desktop: 237, mobile: 120 },
]
export function BarChartDemo() {
return (
<ChartContainer config={chartConfig} className="min-h-[200px] w-full">
<BarChart data={chartData}>
<CartesianGrid vertical={false} />
<XAxis dataKey="month" tickLine={false} axisLine={false} />
<Bar dataKey="desktop" fill="var(--color-desktop)" radius={4} />
<Bar dataKey="mobile" fill="var(--color-mobile)" radius={4} />
<ChartTooltip content={<ChartTooltipContent />} />
</BarChart>
</ChartContainer>
)
}
```
--------------------------------
### shadcn/ui Chart Component - ChartConfig with Custom Colors
Source: https://ui.shadcn.com/docs/components/chart
You can define custom colors directly in the configuration using hex values or CSS variables.
```tsx
const chartConfig = {
desktop: {
label: "Desktop",
color: "#2563eb",
theme: {
light: "#2563eb",
dark: "#60a5fa",
},
},
mobile: {
label: "Mobile",
color: "var(--chart-2)",
},
} satisfies import("@/components/ui/chart").ChartConfig
```
--------------------------------
### shadcn/ui Chart Component - CSS Variables
Source: https://ui.shadcn.com/docs/components/chart
Add chart color variables to your globals.css for consistent theming.
```css
:root {
/* Chart colors */
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.546 0.198 38.228);
--chart-4: oklch(0.596 0.151 343.253);
--chart-5: oklch(0.546 0.158 49.157);
}
.dark {
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.698 0.141 24.311);
--chart-4: oklch(0.676 0.172 171.196);
--chart-5: oklch(0.578 0.192 302.85);
}
```
--------------------------------
### shadcn/ui Chart Component - Line Chart Example
Source: https://ui.shadcn.com/docs/components/chart
Creating a line chart with shadcn/ui charts component.
```tsx
import { Line, LineChart, CartesianGrid, XAxis, YAxis } from "recharts"
import { ChartContainer, ChartTooltipContent } from "@/components/ui/chart"
const chartConfig = {
price: {
label: "Price",
color: "var(--chart-1)",
},
} satisfies import("@/components/ui/chart").ChartConfig
const chartData = [
{ month: "January", price: 186 },
{ month: "February", price: 305 },
{ month: "March", price: 237 },
{ month: "April", price: 203 },
{ month: "May", price: 276 },
]
export function LineChartDemo() {
return (
<ChartContainer config={chartConfig} className="min-h-[200px]">
<LineChart data={chartData}>
<CartesianGrid vertical={false} />
<XAxis dataKey="month" tickLine={false} axisLine={false} />
<YAxis tickLine={false} axisLine={false} tickFormatter={(value) => `$${value}`} />
<Line
dataKey="price"
stroke="var(--color-price)"
strokeWidth={2}
dot={false}
/>
<ChartTooltip content={<ChartTooltipContent />} />
</LineChart>
</ChartContainer>
)
}
```
--------------------------------
### shadcn/ui Chart Component - Area Chart Example
Source: https://ui.shadcn.com/docs/components/chart
Creating an area chart with gradient fill and legend.
```tsx
import { Area, AreaChart, XAxis, YAxis } from "recharts"
import {
ChartContainer,
ChartLegend,
ChartLegendContent,
ChartTooltipContent,
} from "@/components/ui/chart"
const chartConfig = {
desktop: { label: "Desktop", color: "var(--chart-1)" },
mobile: { label: "Mobile", color: "var(--chart-2)" },
} satisfies import("@/components/ui/chart").ChartConfig
export function AreaChartDemo() {
return (
<ChartContainer config={chartConfig} className="min-h-[200px]">
<AreaChart data={chartData}>
<XAxis dataKey="month" tickLine={false} axisLine={false} />
<YAxis tickLine={false} axisLine={false} />
<Area
dataKey="desktop"
fill="var(--color-desktop)"
stroke="var(--color-desktop)"
fillOpacity={0.3}
/>
<Area
dataKey="mobile"
fill="var(--color-mobile)"
stroke="var(--color-mobile)"
fillOpacity={0.3}
/>
<ChartTooltip content={<ChartTooltipContent />} />
<ChartLegend content={<ChartLegendContent />} />
</AreaChart>
</ChartContainer>
)
}
```
--------------------------------
### shadcn/ui Chart Component - Pie Chart Example
Source: https://ui.shadcn.com/docs/components/chart
Creating a pie/donut chart with shadcn/ui.
```tsx
import { Pie, PieChart } from "recharts"
import {
ChartContainer,
ChartLegend,
ChartLegendContent,
ChartTooltipContent,
} from "@/components/ui/chart"
const chartConfig = {
chrome: { label: "Chrome", color: "var(--chart-1)" },
safari: { label: "Safari", color: "var(--chart-2)" },
firefox: { label: "Firefox", color: "var(--chart-3)" },
} satisfies import("@/components/ui/chart").ChartConfig
const pieData = [
{ browser: "Chrome", visitors: 275, fill: "var(--color-chrome)" },
{ browser: "Safari", visitors: 200, fill: "var(--color-safari)" },
{ browser: "Firefox", visitors: 187, fill: "var(--color-firefox)" },
]
export function PieChartDemo() {
return (
<ChartContainer config={chartConfig} className="min-h-[200px]">
<PieChart>
<Pie
data={pieData}
dataKey="visitors"
nameKey="browser"
cx="50%"
cy="50%"
outerRadius={80}
/>
<ChartTooltip content={<ChartTooltipContent />} />
<ChartLegend content={<ChartLegendContent />} />
</PieChart>
</ChartContainer>
)
}
```
--------------------------------
### shadcn/ui ChartTooltipContent Props
Source: https://ui.shadcn.com/docs/components/chart
The ChartTooltipContent component accepts these props for customizing tooltip behavior.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `labelKey` | string | "label" | Key for tooltip label |
| `nameKey` | string | "name" | Key for tooltip name |
| `indicator` | "dot" \| "line" \| "dashed" | "dot" | Indicator style |
| `hideLabel` | boolean | false | Hide label |
| `hideIndicator` | boolean | false | Hide indicator |
--------------------------------
### shadcn/ui Chart Component - Accessibility
Source: https://ui.shadcn.com/docs/components/chart
Enable keyboard navigation and screen reader support by adding the accessibilityLayer prop.
```tsx
<BarChart accessibilityLayer data={chartData}>
<CartesianGrid vertical={false} />
<XAxis dataKey="month" />
<Bar dataKey="desktop" fill="var(--color-desktop)" />
<ChartTooltip content={<ChartTooltipContent />} />
</BarChart>
```
This adds:
- Keyboard arrow key navigation
- ARIA labels for chart elements
- Screen reader announcements for data values
--------------------------------
### shadcn/ui Chart Component - Recharts Dependencies
Source: https://ui.shadcn.com/docs/components/chart
The chart component requires the following Recharts dependencies to be installed.
```bash
pnpm add recharts
npm install recharts
yarn add recharts
```
Recharts provides the following chart types:
- Area, Bar, Line, Pie, Composed
- Radar, RadialBar, Scatter
- Funnel, Treemap

View file

@ -0,0 +1,145 @@
# shadcn/ui Learning Guide
This guide helps you learn shadcn/ui from basics to advanced patterns.
## Learning Path
### 1. Understanding the Philosophy
shadcn/ui is different from traditional component libraries:
- **Copy-paste components**: Components are copied into your project, not installed as packages
- **Full customization**: You own the code and can modify it freely
- **Built on Radix UI**: Provides accessibility primitives
- **Styled with Tailwind**: Uses utility classes for consistent styling
### 2. Core Concepts to Master
#### Class Variance Authority (CVA)
Most components use CVA for variant management:
```tsx
const buttonVariants = cva(
"base-classes",
{
variants: {
variant: {
default: "variant-classes",
destructive: "destructive-classes",
},
size: {
default: "size-classes",
sm: "small-classes",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
```
#### cn Utility Function
The `cn` function combines classes and resolves conflicts:
```tsx
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
```
### 3. Installation Checklist
- [ ] Initialize a new project (Next.js, Vite, or Remix)
- [ ] Install Tailwind CSS
- [ ] Run `npx shadcn@latest init`
- [ ] Configure CSS variables
- [ ] Install first component: `npx shadcn@latest add button`
### 4. Essential Components to Learn First
1. **Button** - Learn variants and sizes
2. **Input** - Form inputs with labels
3. **Card** - Container components
4. **Form** - Form handling with React Hook Form
5. **Dialog** - Modal windows
6. **Select** - Dropdown selections
7. **Toast** - Notifications
### 5. Common Patterns
#### Form Pattern
Every form follows this structure:
```tsx
1. Define Zod schema
2. Create form with useForm
3. Wrap with Form component
4. Add FormField for each input
5. Handle submission
```
#### Component Customization Pattern
To customize a component:
1. Copy component to your project
2. Modify the variants
3. Add new props if needed
4. Update types
### 6. Best Practices
- Always use TypeScript
- Follow the existing component structure
- Use semantic HTML when possible
- Test with screen readers for accessibility
- Keep components small and focused
### 7. Advanced Topics
- Creating custom components from scratch
- Building complex forms with validation
- Implementing dark mode
- Optimizing for performance
- Testing components
## Practice Exercises
### Exercise 1: Basic Setup
1. Create a new Next.js project
2. Set up shadcn/ui
3. Install and customize a Button component
4. Add a new variant "gradient"
### Exercise 2: Form Building
1. Create a contact form with:
- Name input (required)
- Email input (email validation)
- Message textarea (min length)
- Submit button with loading state
### Exercise 3: Component Combination
1. Build a settings page using:
- Card for layout
- Sheet for mobile menu
- Select for dropdowns
- Switch for toggles
- Toast for notifications
### Exercise 4: Custom Component
1. Create a custom Badge component
2. Support variants: default, secondary, destructive, outline
3. Support sizes: sm, default, lg
4. Add icon support
## Resources
- [Official Documentation](https://ui.shadcn.com)
- [GitHub Repository](https://github.com/shadcn/ui)
- [Examples Gallery](https://ui.shadcn.com/examples)
- [Radix UI Primitives](https://www.radix-ui.com/primitives)
- [Tailwind CSS Documentation](https://tailwindcss.com/docs)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,586 @@
# shadcn.io Component Library
shadcn.io is a comprehensive React UI component library built on shadcn/ui principles, providing developers with production-ready, composable components for modern web applications. The library serves as a centralized resource for React developers who need high-quality UI components with TypeScript support, ranging from basic interactive elements to advanced AI-powered integrations. Unlike traditional component libraries that require package installations, shadcn.io components are designed to be copied directly into your project, giving you full control and customization capabilities.
The library encompasses four major categories: composable UI components (terminal, dock, credit cards, QR codes, color pickers), chart components built with Recharts, animation components with Tailwind CSS integration, and custom React hooks for state management and lifecycle operations. Each component follows best practices for accessibility, performance, and developer experience, with comprehensive TypeScript definitions and Next.js compatibility. The platform emphasizes flexibility and customization, allowing developers to modify components at the source level rather than being constrained by package APIs.
## Core Components
### Terminal Component
Interactive terminal emulator with typing animations and command execution simulation for developer-focused interfaces.
```tsx
import { Terminal } from "@/components/ui/terminal"
export default function DemoTerminal() {
return (
npm install @repo/terminalInstalling dependencies...npm start
)
}
```
### Dock Component
macOS-style application dock with smooth magnification effects on hover, perfect for navigation menus.
```tsx
import { Dock, DockIcon } from "@/components/ui/dock"
import { Home, Settings, User, Mail } from "lucide-react"
export default function AppDock() {
return (
)
}
```
### Credit Card Component
Interactive 3D credit card component with flip animations for payment forms and card displays.
```tsx
import { CreditCard } from "@/components/ui/credit-card"
import { useState } from "react"
export default function PaymentForm() {
const [cardData, setCardData] = useState({
number: "4532 1234 5678 9010",
holder: "JOHN DOE",
expiry: "12/28",
cvv: "123"
})
return (
console.log("Card flipped:", flipped)}
/>
)
}
```
### Image Zoom Component
Zoomable image component with smooth modal transitions for image galleries and product displays.
```tsx
import { ImageZoom } from "@/components/ui/image-zoom"
export default function ProductGallery() {
return (
)
}
```
### QR Code Component
Generate and display customizable QR codes with styling options for links, contact information, and authentication.
```tsx
import { QRCode } from "@/components/ui/qr-code"
export default function ShareDialog() {
const shareUrl = "https://shadcn.io"
return (
Scan to visit shadcn.io
)
}
```
### Color Picker Component
Advanced color selection component supporting multiple color formats (HEX, RGB, HSL) with preview.
```tsx
import { ColorPicker } from "@/components/ui/color-picker"
import { useState } from "react"
export default function ThemeCustomizer() {
const [color, setColor] = useState("#3b82f6")
return (
Selected: {color}
)
}
```
## Chart Components
### Bar Chart Component
Clean bar chart component for data comparison and categorical analysis using Recharts.
```tsx
import { BarChart } from "@/components/ui/bar-chart"
export default function SalesChart() {
const data = [
{ month: "Jan", sales: 4000, revenue: 2400 },
{ month: "Feb", sales: 3000, revenue: 1398 },
{ month: "Mar", sales: 2000, revenue: 9800 },
{ month: "Apr", sales: 2780, revenue: 3908 },
{ month: "May", sales: 1890, revenue: 4800 },
{ month: "Jun", sales: 2390, revenue: 3800 }
]
return (
`$${value.toLocaleString()}`}
yAxisWidth={60}
/>
)
}
```
### Line Chart Component
Smooth line chart for visualizing trends and time-series data with multiple data series support.
```tsx
import { LineChart } from "@/components/ui/line-chart"
export default function MetricsChart() {
const data = [
{ date: "2024-01", users: 1200, sessions: 3400 },
{ date: "2024-02", users: 1800, sessions: 4200 },
{ date: "2024-03", users: 2400, sessions: 5800 },
{ date: "2024-04", users: 3100, sessions: 7200 },
{ date: "2024-05", users: 3800, sessions: 8900 }
]
return (
)
}
```
### Pie Chart Component
Donut chart component for displaying proportional data and percentage distributions.
```tsx
import { PieChart } from "@/components/ui/pie-chart"
export default function MarketShareChart() {
const data = [
{ name: "Product A", value: 400, fill: "#3b82f6" },
{ name: "Product B", value: 300, fill: "#10b981" },
{ name: "Product C", value: 300, fill: "#f59e0b" },
{ name: "Product D", value: 200, fill: "#ef4444" }
]
return (
`${entry.name}: ${entry.value}`}
/>
)
}
```
### Area Chart Component
Stacked area chart for visualizing volume changes over time with multiple data series.
```tsx
import { AreaChart } from "@/components/ui/area-chart"
export default function TrafficChart() {
const data = [
{ month: "Jan", mobile: 2000, desktop: 3000, tablet: 1000 },
{ month: "Feb", mobile: 2200, desktop: 3200, tablet: 1100 },
{ month: "Mar", mobile: 2800, desktop: 3800, tablet: 1300 },
{ month: "Apr", mobile: 3200, desktop: 4200, tablet: 1500 },
{ month: "May", mobile: 3800, desktop: 4800, tablet: 1800 }
]
return (
)
}
```
### Radar Chart Component
Multi-axis chart for comparing multiple variables across different categories simultaneously.
```tsx
import { RadarChart } from "@/components/ui/radar-chart"
export default function SkillsChart() {
const data = [
{ skill: "JavaScript", score: 85, industry: 75 },
{ skill: "TypeScript", score: 80, industry: 70 },
{ skill: "React", score: 90, industry: 80 },
{ skill: "Node.js", score: 75, industry: 72 },
{ skill: "CSS", score: 88, industry: 78 }
]
return (
)
}
```
### Mixed Chart Component
Combined bar and line chart for displaying multiple data types with different visualization methods.
```tsx
import { MixedChart } from "@/components/ui/mixed-chart"
export default function PerformanceChart() {
const data = [
{ month: "Jan", revenue: 4000, growth: 5.2 },
{ month: "Feb", revenue: 4200, growth: 5.0 },
{ month: "Mar", revenue: 4800, growth: 14.3 },
{ month: "Apr", revenue: 5200, growth: 8.3 },
{ month: "May", revenue: 5800, growth: 11.5 }
]
return (
)
}
```
## Animation Components
### Magnetic Effect Component
Magnetic hover effect that smoothly follows cursor movement for interactive buttons and cards.
```tsx
import { Magnetic } from "@/components/ui/magnetic"
export default function InteractiveButton() {
return (
Hover me
)
}
```
### Animated Cursor Component
Custom animated cursor with interactive effects and particle trails for immersive experiences.
```tsx
import { AnimatedCursor } from "@/components/ui/animated-cursor"
export default function Layout({ children }) {
return (
<>
{children}
)
}
```
### Apple Hello Effect Component
Recreation of Apple's iconic "hello" animation with multi-language text transitions.
```tsx
import { AppleHello } from "@/components/ui/apple-hello"
export default function WelcomeScreen() {
const greetings = [
{ text: "Hello", lang: "en" },
{ text: "Bonjour", lang: "fr" },
{ text: "こんにちは", lang: "ja" },
{ text: "Hola", lang: "es" },
{ text: "你好", lang: "zh" }
]
return (
)
}
```
### Liquid Button Component
Button with fluid liquid animation effect on hover for engaging call-to-action elements.
```tsx
import { LiquidButton } from "@/components/ui/liquid-button"
export default function CTASection() {
return (
console.log("CTA clicked")}
>
Get Started
)
}
```
### Rolling Text Component
Text animation that creates a rolling effect with smooth character transitions.
```tsx
import { RollingText } from "@/components/ui/rolling-text"
export default function AnimatedHeading() {
return (
)
}
```
### Shimmering Text Component
Text with animated shimmer effect for attention-grabbing headings and highlights.
```tsx
import { ShimmeringText } from "@/components/ui/shimmering-text"
export default function Hero() {
return (
)
}
```
## React Hooks
### useBoolean Hook
Enhanced boolean state management with toggle, enable, and disable methods for cleaner component logic.
```tsx
import { useBoolean } from "@/hooks/use-boolean"
export default function TogglePanel() {
const modal = useBoolean(false)
const loading = useBoolean(false)
const handleSubmit = async () => {
loading.setTrue()
try {
await submitForm()
modal.setFalse()
} finally {
loading.setFalse()
}
}
return (
<>
Toggle Modal
{modal.value && (
Status: {loading.value ? "Saving..." : "Ready"}
Submit
)}
)
}
```
### useCounter Hook
Counter hook with increment, decrement, reset, and set functionality for numeric state management.
```tsx
import { useCounter } from "@/hooks/use-counter"
export default function CartCounter() {
const quantity = useCounter(0, { min: 0, max: 99 })
return (
-
{quantity.value}
+
Reset
)
}
```
### useLocalStorage Hook
Persist state in browser localStorage with automatic serialization and deserialization.
```tsx
import { useLocalStorage } from "@/hooks/use-local-storage"
export default function UserPreferences() {
const [theme, setTheme] = useLocalStorage("theme", "light")
const [settings, setSettings] = useLocalStorage("settings", {
notifications: true,
emailUpdates: false
})
return (
setTheme(e.target.value)}>
LightDark setSettings({
...settings,
notifications: e.target.checked
})}
/>
Enable Notifications
)
}
```
### useDebounceValue Hook
Debounce values to prevent excessive updates and API calls during rapid user input.
```tsx
import { useDebounceValue } from "@/hooks/use-debounce-value"
import { useState, useEffect } from "react"
export default function SearchBox() {
const [search, setSearch] = useState("")
const debouncedSearch = useDebounceValue(search, 500)
const [results, setResults] = useState([])
const [apiCalls, setApiCalls] = useState(0)
useEffect(() => {
if (debouncedSearch) {
setApiCalls(prev => prev + 1)
fetch(`/api/search?q=${debouncedSearch}`)
.then(res => res.json())
.then(setResults)
}
}, [debouncedSearch])
return (
setSearch(e.target.value)}
placeholder="Search..."
/>
API calls: {apiCalls}
)
}
```
### useHover Hook
Track hover state on elements with customizable enter and leave delays for tooltip and preview functionality.
```tsx
import { useHover } from "@/hooks/use-hover"
import { useRef } from "react"
export default function ImagePreview() {
const hoverRef = useRef(null)
const isHovering = useHover(hoverRef, {
enterDelay: 200,
leaveDelay: 100
})
return (
![Preview](http://https:%2F%2Fcontext7.com%2Fwebsites%2Fshadcn_io%2Fllms.txt/thumbnail.jpg)
{isHovering && (
![Full size](http://https:%2F%2Fcontext7.com%2Fwebsites%2Fshadcn_io%2Fllms.txt/full-size.jpg)
)}
)
}
```
### useCountdown Hook
Countdown timer with play, pause, reset controls and completion callbacks for time-limited features.
```tsx
import { useCountdown } from "@/hooks/use-countdown"
export default function OTPTimer() {
const countdown = useCountdown({
initialSeconds: 60,
onComplete: () => alert("OTP expired! Request a new code.")
})
return (
{countdown.seconds}s
{!countdown.isRunning ? (
Start
) : (
Pause
)}
Reset
Status: {countdown.isComplete ? "Expired" : countdown.isRunning ? "Active" : "Paused"}
)
}
```
## Installation and Usage
### CLI Installation
Install components directly into your project using the shadcn CLI for instant integration.
```bash
# Initialize shadcn in your project
npx shadcn@latest init
# Add individual components
npx shadcn@latest add terminal
npx shadcn@latest add dock
npx shadcn@latest add credit-card
# Add multiple components at once
npx shadcn@latest add bar-chart line-chart pie-chart
# Add hooks
npx shadcn@latest add use-boolean use-counter use-local-storage
```
### Project Configuration
Configure your project to work with shadcn.io components using TypeScript and Tailwind CSS.
```typescript
// tailwind.config.ts
import type { Config } from "tailwindcss"
const config: Config = {
darkMode: ["class"],
content: [
"./pages/**/*.{ts,tsx}",
"./components/**/*.{ts,tsx}",
"./app/**/*.{ts,tsx}",
],
theme: {
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
},
},
},
plugins: [require("tailwindcss-animate")],
}
export default config
```
## Summary
The shadcn.io component library serves as a comprehensive toolkit for React developers building modern web applications with Next.js and TypeScript. The library's primary use cases include rapid prototyping of user interfaces, building data-rich dashboards with interactive charts, creating engaging user experiences with animations and effects, and implementing common UI patterns without writing boilerplate code. The copy-paste approach gives developers complete ownership of their components, allowing for deep customization while maintaining consistency with shadcn/ui design principles. Components are particularly well-suited for SaaS applications, admin panels, marketing websites, and e-commerce platforms that require professional, accessible UI elements.
Integration patterns center around composability and customization rather than rigid package dependencies. Developers can cherry-pick individual components using the CLI, modify them at the source level to match their design system, and combine them with existing shadcn/ui components for a cohesive interface. The library supports both light and dark themes through CSS variables, integrates seamlessly with Tailwind CSS utility classes, and follows React best practices for performance and accessibility. Custom hooks provide reusable logic patterns that complement the visual components, creating a complete ecosystem for building feature-rich applications. The TypeScript-first approach ensures type safety throughout the development process, while the Recharts integration for data visualization provides powerful charting capabilities without additional configuration overhead.

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,7 @@
import { activityEventBus } from '../../../lib/realtime';
export const dynamic = 'force-dynamic';
export async function GET(request: Request): Promise<Response> {
const url = new URL(request.url);
const projectRoot = url.searchParams.get('projectRoot') || undefined;

View file

@ -3,6 +3,8 @@ import { readIssuesFromDisk } from '../../../../../lib/read-issues';
import { activityEventBus } from '../../../../../lib/realtime';
import { getAgentMetrics } from '../../../../../lib/agent-sessions';
export const dynamic = 'force-dynamic';
export async function GET(
request: Request,
{ params }: { params: Promise<{ agentId: string }> }

View file

@ -1,6 +1,8 @@
import { NextResponse } from 'next/server';
import { listAgents } from '../../../../lib/agent-registry';
export const dynamic = 'force-dynamic';
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const projectRoot = searchParams.get('projectRoot');

View file

@ -1,6 +1,8 @@
import { NextRequest, NextResponse } from 'next/server';
import { readInteractionsViaBd } from '../../../../../lib/read-interactions';
export const dynamic = 'force-dynamic';
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }

View file

@ -2,6 +2,8 @@ import { NextResponse } from 'next/server';
import { readIssuesFromDisk } from '../../../../lib/read-issues';
export const dynamic = 'force-dynamic';
export async function GET(request: Request): Promise<Response> {
const url = new URL(request.url);
const projectRoot = url.searchParams.get('projectRoot') ?? process.cwd();

View file

@ -21,11 +21,15 @@ async function readLastTouchedVersion(filePath: string): Promise<number | null>
}
}
export const dynamic = 'force-dynamic';
export async function GET(request: Request): Promise<Response> {
const url = new URL(request.url);
const projectRootSearchParam = url.searchParams.get('projectRoot');
const projectRoot = canonicalizeWindowsPath(projectRootSearchParam || process.cwd());
console.log(`[SSE /api/events] Connection request - raw param: "${projectRootSearchParam}", canonicalized: "${projectRoot}"`);
try {
getIssuesWatchManager().startWatch(projectRoot);
} catch (error) {
@ -41,7 +45,7 @@ export async function GET(request: Request): Promise<Response> {
);
}
let cleanup = () => {};
let cleanup = () => { };
const stream = new ReadableStream<Uint8Array>({
start(controller) {
@ -55,15 +59,20 @@ export async function GET(request: Request): Promise<Response> {
write(SSE_CONNECTED_FRAME);
console.log(`[SSE /api/events] Subscribing to event bus with projectRoot: ${projectRoot}`);
const unsubscribeIssues = issuesEventBus.subscribe(
(event) => {
console.log('[SSE /api/events] Received ISSUES event from bus:', event.kind, 'projectRoot:', event.projectRoot);
write(toSseFrame(event));
},
{ projectRoot },
);
console.log(`[SSE /api/events] Subscriber count after subscribe: ${issuesEventBus.getSubscriberCount()}`);
const unsubscribeActivity = activityEventBus.subscribe(
(event) => {
console.log('[SSE /api/events] Received ACTIVITY event from bus');
write(toActivitySseFrame(event));
},
{ projectRoot },

View file

@ -1,6 +1,8 @@
import { NextResponse } from 'next/server';
import { runBdCommand } from '../../../../lib/bridge';
export const dynamic = 'force-dynamic';
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const projectRoot = searchParams.get('projectRoot');
@ -23,24 +25,24 @@ export async function GET(request: Request) {
});
if (!headResult.success) {
return NextResponse.json({ ok: false, error: 'Failed to fetch mission head' }, { status: 500 });
return NextResponse.json({ ok: false, error: 'Failed to fetch mission head' }, { status: 500 });
}
try {
const head = JSON.parse(headResult.stdout);
let children = [];
if (childrenResult.success && childrenResult.stdout.trim()) {
children = JSON.parse(childrenResult.stdout);
children = JSON.parse(childrenResult.stdout);
}
const headObj = Array.isArray(head) ? head[0] : head;
// Transform for graph view (if needed, or just return raw issues and let UI handle it)
// The WorkflowGraph component expects BeadIssue[]
const nodes = [headObj, ...children];
return NextResponse.json({ ok: true, data: { nodes } });
} catch (e) {
} catch {
return NextResponse.json({ ok: false, error: 'Failed to parse graph data' }, { status: 500 });
}
}

View file

@ -0,0 +1,53 @@
import { NextRequest, NextResponse } from 'next/server';
import { saveArchetype, deleteArchetype } from '../../../../../lib/server/beads-fs';
export async function PUT(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params;
const body = await request.json();
// Validation
if (!body.name || !body.systemPrompt) {
return NextResponse.json(
{ error: 'name and systemPrompt are required' },
{ status: 400 }
);
}
const archetype = await saveArchetype({
id,
name: body.name,
description: body.description || '',
systemPrompt: body.systemPrompt,
capabilities: body.capabilities || [],
color: body.color || '#3b82f6',
createdAt: body.createdAt,
isBuiltIn: body.isBuiltIn
});
return NextResponse.json(archetype);
} catch (error) {
return NextResponse.json(
{ error: error instanceof Error ? error.message : 'Failed to update archetype' },
{ status: 500 }
);
}
}
export async function DELETE(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params;
await deleteArchetype(id);
return NextResponse.json({ success: true });
} catch (error) {
const message = error instanceof Error ? error.message : 'Failed to delete archetype';
const status = message.includes('built-in') ? 403 : 404;
return NextResponse.json({ error: message }, { status });
}
}

View file

@ -1,7 +1,38 @@
import { NextResponse } from 'next/server';
import { getArchetypes } from '../../../../lib/server/beads-fs';
import { NextRequest, NextResponse } from 'next/server';
import { getArchetypes, saveArchetype } from '../../../../lib/server/beads-fs';
export const dynamic = 'force-dynamic';
export async function GET() {
const data = await getArchetypes();
return NextResponse.json(data);
}
export async function POST(request: NextRequest) {
try {
const body = await request.json();
// Validation
if (!body.name || !body.systemPrompt) {
return NextResponse.json(
{ error: 'name and systemPrompt are required' },
{ status: 400 }
);
}
const archetype = await saveArchetype({
name: body.name,
description: body.description || '',
systemPrompt: body.systemPrompt,
capabilities: body.capabilities || [],
color: body.color || '#3b82f6'
});
return NextResponse.json(archetype, { status: 201 });
} catch (error) {
return NextResponse.json(
{ error: error instanceof Error ? error.message : 'Failed to create archetype' },
{ status: 500 }
);
}
}

View file

@ -1,6 +1,8 @@
import { NextResponse } from 'next/server';
import { runBdCommand } from '../../../../lib/bridge';
export const dynamic = 'force-dynamic';
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const projectRoot = searchParams.get('projectRoot');
@ -23,7 +25,7 @@ export async function GET(request: Request) {
// If output is empty or not JSON array, handle gracefully
const json = JSON.parse(result.stdout || '[]');
return NextResponse.json({ ok: true, data: json });
} catch (e) {
} catch {
return NextResponse.json({ ok: false, error: 'Failed to parse formulas' }, { status: 500 });
}
}

View file

@ -1,6 +1,8 @@
import { NextResponse } from 'next/server';
import { runBdCommand } from '../../../../lib/bridge';
export const dynamic = 'force-dynamic';
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const projectRoot = searchParams.get('projectRoot');
@ -23,7 +25,7 @@ export async function GET(request: Request) {
});
if (!epicResult.success) {
return NextResponse.json({ ok: false, error: 'Failed to fetch epic' }, { status: 500 });
return NextResponse.json({ ok: false, error: 'Failed to fetch epic' }, { status: 500 });
}
try {
@ -31,15 +33,15 @@ export async function GET(request: Request) {
// Handle list returning empty or error gracefully
let children = [];
if (childrenResult.success && childrenResult.stdout.trim()) {
children = JSON.parse(childrenResult.stdout);
// bd list returns array, bd show returns object (or array of 1)
children = JSON.parse(childrenResult.stdout);
// bd list returns array, bd show returns object (or array of 1)
}
const epicObj = Array.isArray(epic) ? epic[0] : epic;
const issues = [epicObj, ...children];
return NextResponse.json({ ok: true, data: issues });
} catch (e) {
} catch {
return NextResponse.json({ ok: false, error: 'Failed to parse graph data' }, { status: 500 });
}
}

View file

@ -2,6 +2,8 @@ import { NextResponse } from 'next/server';
import { runBdCommand } from '../../../../lib/bridge';
export const dynamic = 'force-dynamic';
export async function GET(request: Request): Promise<Response> {
const { searchParams } = new URL(request.url);
const projectRoot = searchParams.get('projectRoot');
@ -29,11 +31,11 @@ export async function GET(request: Request): Promise<Response> {
const rawData = JSON.parse(result.stdout);
// Filter out items that look like agents (start with "Agent:" or have gt:agent style IDs if discernible)
// Real swarms/molecules usually don't start with "Agent:".
const swarms = (rawData.swarms || []).filter((s: any) =>
!s.title.startsWith('Agent: ') &&
const swarms = (rawData.swarms || []).filter((s: any) =>
!s.title.startsWith('Agent: ') &&
!s.title.startsWith('Agent:')
);
return NextResponse.json({ ok: true, data: { swarms } });
} catch {
return NextResponse.json(

View file

@ -2,6 +2,8 @@ import { NextResponse } from 'next/server';
import { runBdCommand } from '../../../../lib/bridge';
export const dynamic = 'force-dynamic';
export async function GET(request: Request): Promise<Response> {
const { searchParams } = new URL(request.url);
const projectRoot = searchParams.get('projectRoot');

View file

@ -0,0 +1,59 @@
import { NextRequest, NextResponse } from 'next/server';
import { saveTemplate, deleteTemplate } from '../../../../../lib/server/beads-fs';
export async function PUT(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params;
const body = await request.json();
// Validation
if (!body.name) {
return NextResponse.json(
{ error: 'name is required' },
{ status: 400 }
);
}
if (!body.team || !Array.isArray(body.team) || body.team.length === 0) {
return NextResponse.json(
{ error: 'team must be a non-empty array' },
{ status: 400 }
);
}
const template = await saveTemplate({
id,
name: body.name,
description: body.description || '',
team: body.team,
protoFormula: body.protoFormula,
createdAt: body.createdAt,
isBuiltIn: body.isBuiltIn
});
return NextResponse.json(template);
} catch (error) {
return NextResponse.json(
{ error: error instanceof Error ? error.message : 'Failed to update template' },
{ status: 500 }
);
}
}
export async function DELETE(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params;
await deleteTemplate(id);
return NextResponse.json({ success: true });
} catch (error) {
const message = error instanceof Error ? error.message : 'Failed to delete template';
const status = message.includes('built-in') ? 403 : 404;
return NextResponse.json({ error: message }, { status });
}
}

View file

@ -1,7 +1,44 @@
import { NextResponse } from 'next/server';
import { getTemplates } from '../../../../lib/server/beads-fs';
import { NextRequest, NextResponse } from 'next/server';
import { getTemplates, saveTemplate } from '../../../../lib/server/beads-fs';
export const dynamic = 'force-dynamic';
export async function GET() {
const data = await getTemplates();
return NextResponse.json(data);
}
export async function POST(request: NextRequest) {
try {
const body = await request.json();
// Validation
if (!body.name) {
return NextResponse.json(
{ error: 'name is required' },
{ status: 400 }
);
}
if (!body.team || !Array.isArray(body.team) || body.team.length === 0) {
return NextResponse.json(
{ error: 'team must be a non-empty array' },
{ status: 400 }
);
}
const template = await saveTemplate({
name: body.name,
description: body.description || '',
team: body.team,
protoFormula: body.protoFormula
});
return NextResponse.json(template, { status: 201 });
} catch (error) {
return NextResponse.json(
{ error: error instanceof Error ? error.message : 'Failed to create template' },
{ status: 500 }
);
}
}

View file

@ -189,7 +189,7 @@ export default function MockupPage() {
setDraftLabels(selectedTask.labels.join(", "))
setDraftBlockedReason(selectedTask.blockedReason)
setThreadEditMode(false)
}, [selectedTask?.id])
}, [selectedTask, selectedTask?.id])
const saveTaskChanges = () => {
if (!selectedTask) return
@ -202,26 +202,26 @@ export default function MockupPage() {
epic.id !== selectedEpicId
? epic
: {
...epic,
tasks: epic.tasks.map((task) =>
task.id !== selectedTask.id
? task
: {
...task,
title: draftTitle,
description: draftDescription,
status: draftStatus,
priority: draftPriority,
issueType: draftIssueType,
assignee: draftAssignee,
owner: draftOwner,
labels: nextLabels,
blockedReason: draftBlockedReason,
updatedAgo: "now",
blockedByCount: draftStatus === "blocked" ? Math.max(task.blockedByCount, 1) : 0,
}
),
}
...epic,
tasks: epic.tasks.map((task) =>
task.id !== selectedTask.id
? task
: {
...task,
title: draftTitle,
description: draftDescription,
status: draftStatus,
priority: draftPriority,
issueType: draftIssueType,
assignee: draftAssignee,
owner: draftOwner,
labels: nextLabels,
blockedReason: draftBlockedReason,
updatedAgo: "now",
blockedByCount: draftStatus === "blocked" ? Math.max(task.blockedByCount, 1) : 0,
}
),
}
)
)
setSavePulse(true)
@ -250,29 +250,29 @@ export default function MockupPage() {
<Card className={panelClass} style={{ backgroundColor: palette.surface, borderColor: palette.border }}>
<CardHeader className="pb-3">
<div className="flex items-center justify-between">
{leftMode === "tasks" ? (
<Button variant="ghost" className="h-8 px-2" onClick={() => setLeftMode("epics")}>
<ArrowLeft className="mr-2 h-4 w-4" /> Back to epics
</Button>
) : (
<CardTitle className="text-lg">Epics</CardTitle>
)}
<Badge className="rounded-full" style={{ backgroundColor: palette.mutedBg, color: palette.textSecondary }}>{selectedEpic.openCount} open</Badge>
{leftMode === "tasks" ? (
<Button variant="ghost" className="h-8 px-2" onClick={() => setLeftMode("epics")}>
<ArrowLeft className="mr-2 h-4 w-4" /> Back to epics
</Button>
) : (
<CardTitle className="text-lg">Epics</CardTitle>
)}
<Badge className="rounded-full" style={{ backgroundColor: palette.mutedBg, color: palette.textSecondary }}>{selectedEpic.openCount} open</Badge>
</div>
<CardDescription style={{ color: palette.textSecondary }}>Select an epic, then choose a task.</CardDescription>
</CardHeader>
<CardContent className="pt-0">
<Input
value={query}
onChange={(event) => setQuery(event.target.value)}
placeholder={leftMode === "epics" ? "Search epics" : "Search tasks"}
className="mb-3"
style={{ backgroundColor: palette.mutedBg, borderColor: palette.border }}
/>
<ScrollArea className="h-[520px] pr-2">
<div className="space-y-2">
{leftMode === "epics"
? epics
<Input
value={query}
onChange={(event) => setQuery(event.target.value)}
placeholder={leftMode === "epics" ? "Search epics" : "Search tasks"}
className="mb-3"
style={{ backgroundColor: palette.mutedBg, borderColor: palette.border }}
/>
<ScrollArea className="h-[520px] pr-2">
<div className="space-y-2">
{leftMode === "epics"
? epics
.filter((epic) => epic.name.toLowerCase().includes(query.toLowerCase()))
.map((epic) => (
<button
@ -291,7 +291,7 @@ export default function MockupPage() {
<p className="mt-1 text-xs" style={{ color: palette.textSecondary }}>{epic.id}</p>
</button>
))
: filteredTasks.map((task) => (
: filteredTasks.map((task) => (
<button
key={task.id}
type="button"
@ -299,11 +299,10 @@ export default function MockupPage() {
setSelectedTaskId(task.id)
closeThread()
}}
className={`${subPanelClass} w-full p-3 text-left transition duration-200 ${
selectedTask?.id === task.id
className={`${subPanelClass} w-full p-3 text-left transition duration-200 ${selectedTask?.id === task.id
? "shadow-[0_12px_26px_rgba(0,0,0,0.4)]"
: "hover:-translate-y-[1px] hover:shadow-[0_10px_22px_rgba(0,0,0,0.33)]"
}`}
}`}
style={{
backgroundColor: selectedTask?.id === task.id ? palette.mutedBg : palette.surface,
borderColor: selectedTask?.id === task.id ? palette.primary : palette.border,
@ -318,68 +317,68 @@ export default function MockupPage() {
</div>
</button>
))}
</div>
</ScrollArea>
</div>
</ScrollArea>
</CardContent>
</Card>
<Card className={panelClass} style={{ backgroundColor: palette.surface, borderColor: palette.border }}>
<CardHeader className="pb-3">
<div className="flex items-center justify-between">
<div>
<CardTitle className="text-lg">{selectedEpic.name}</CardTitle>
<CardDescription style={{ color: palette.textSecondary }}>Task cards + thread context</CardDescription>
</div>
<Button className="h-8 rounded-full px-4 text-white" style={{ backgroundColor: palette.primary }}>New update</Button>
<div>
<CardTitle className="text-lg">{selectedEpic.name}</CardTitle>
<CardDescription style={{ color: palette.textSecondary }}>Task cards + thread context</CardDescription>
</div>
<Button className="h-8 rounded-full px-4 text-white" style={{ backgroundColor: palette.primary }}>New update</Button>
</div>
</CardHeader>
<CardContent className="pt-0">
<ScrollArea className="h-[430px] pr-2">
<div className="grid grid-cols-1 gap-3 xl:grid-cols-2">
{filteredTasks.map((task) => (
<button
key={task.id}
type="button"
onClick={() => setSelectedTaskId(task.id)}
className={`rounded-xl border p-4 text-left transition duration-200 hover:-translate-y-[1px] hover:shadow-[0_14px_28px_rgba(0,0,0,0.35)] ${statusClasses(task.status)}`}
style={{ borderColor: selectedTask?.id === task.id ? palette.primary : palette.border }}
>
<div className="flex items-center justify-between">
<span className="text-sm font-semibold" style={{ color: palette.eggplant }}>{task.id}</span>
<Badge className={`rounded-full px-2 py-0.5 text-[11px] ${statusBadge(task.status)}`}>{task.status.replace("_", " ")}</Badge>
</div>
<p className="mt-3 text-[1.7rem] font-semibold leading-[1.15]">{task.title}</p>
<p className="mt-2 line-clamp-2 text-sm" style={{ color: palette.textSecondary }}>{task.description}</p>
<div className="mt-4 flex items-center gap-3 text-xs" style={{ color: palette.textSecondary }}>
<span className="inline-flex items-center gap-1"><Clock3 className="h-3.5 w-3.5" />{task.updatedAgo}</span>
<span className="inline-flex items-center gap-1"><Link2 className="h-3.5 w-3.5" />{task.dependencyCount}</span>
<span className="inline-flex items-center gap-1"><MessageCircle className="h-3.5 w-3.5" />{task.commentCount}</span>
</div>
</button>
))}
</div>
</ScrollArea>
<Separator className="my-4" />
<div className={`${subPanelClass} p-4 shadow-[inset_0_1px_0_rgba(255,255,255,0.03)]`} style={{ borderColor: palette.border, backgroundColor: palette.mutedBg }}>
<div className="mb-2 flex items-center justify-between">
<p className="text-sm font-semibold">Conversation: {selectedTask?.id}</p>
<Button variant="ghost" className="h-7 px-2" style={{ color: palette.secondary }} onClick={() => setThreadOpen(true)}>
Open thread <ArrowUpRight className="ml-1 h-3.5 w-3.5" />
</Button>
</div>
<div className="space-y-2">
<div className="rounded-lg border px-3 py-2 text-sm" style={{ borderColor: "#5A5D6A", backgroundColor: "#2A2B37" }}>
<span className="font-semibold" style={{ color: palette.success }}>alex.chen</span>
<span className="mx-1 text-xs" style={{ color: "#8F92A3" }}>2m</span>
<span style={{ color: palette.textSecondary }}>Need confirmation that detail strip stays sticky while card grid scrolls.</span>
<ScrollArea className="h-[430px] pr-2">
<div className="grid grid-cols-1 gap-3 xl:grid-cols-2">
{filteredTasks.map((task) => (
<button
key={task.id}
type="button"
onClick={() => setSelectedTaskId(task.id)}
className={`rounded-xl border p-4 text-left transition duration-200 hover:-translate-y-[1px] hover:shadow-[0_14px_28px_rgba(0,0,0,0.35)] ${statusClasses(task.status)}`}
style={{ borderColor: selectedTask?.id === task.id ? palette.primary : palette.border }}
>
<div className="flex items-center justify-between">
<span className="text-sm font-semibold" style={{ color: palette.eggplant }}>{task.id}</span>
<Badge className={`rounded-full px-2 py-0.5 text-[11px] ${statusBadge(task.status)}`}>{task.status.replace("_", " ")}</Badge>
</div>
<p className="mt-3 text-[1.7rem] font-semibold leading-[1.15]">{task.title}</p>
<p className="mt-2 line-clamp-2 text-sm" style={{ color: palette.textSecondary }}>{task.description}</p>
<div className="mt-4 flex items-center gap-3 text-xs" style={{ color: palette.textSecondary }}>
<span className="inline-flex items-center gap-1"><Clock3 className="h-3.5 w-3.5" />{task.updatedAgo}</span>
<span className="inline-flex items-center gap-1"><Link2 className="h-3.5 w-3.5" />{task.dependencyCount}</span>
<span className="inline-flex items-center gap-1"><MessageCircle className="h-3.5 w-3.5" />{task.commentCount}</span>
</div>
</button>
))}
</div>
<div className="rounded-lg border px-3 py-2 text-sm" style={{ borderColor: "#5A5D6A", backgroundColor: "#2A2B37" }}>
<span className="font-semibold" style={{ color: palette.secondary }}>sarah.lee</span>
<span className="mx-1 text-xs" style={{ color: "#8F92A3" }}>1m</span>
<span style={{ color: palette.textSecondary }}>Approved if right rail remains visible at 1280px breakpoint.</span>
</ScrollArea>
<Separator className="my-4" />
<div className={`${subPanelClass} p-4 shadow-[inset_0_1px_0_rgba(255,255,255,0.03)]`} style={{ borderColor: palette.border, backgroundColor: palette.mutedBg }}>
<div className="mb-2 flex items-center justify-between">
<p className="text-sm font-semibold">Conversation: {selectedTask?.id}</p>
<Button variant="ghost" className="h-7 px-2" style={{ color: palette.secondary }} onClick={() => setThreadOpen(true)}>
Open thread <ArrowUpRight className="ml-1 h-3.5 w-3.5" />
</Button>
</div>
<div className="space-y-2">
<div className="rounded-lg border px-3 py-2 text-sm" style={{ borderColor: "#5A5D6A", backgroundColor: "#2A2B37" }}>
<span className="font-semibold" style={{ color: palette.success }}>alex.chen</span>
<span className="mx-1 text-xs" style={{ color: "#8F92A3" }}>2m</span>
<span style={{ color: palette.textSecondary }}>Need confirmation that detail strip stays sticky while card grid scrolls.</span>
</div>
<div className="rounded-lg border px-3 py-2 text-sm" style={{ borderColor: "#5A5D6A", backgroundColor: "#2A2B37" }}>
<span className="font-semibold" style={{ color: palette.secondary }}>sarah.lee</span>
<span className="mx-1 text-xs" style={{ color: "#8F92A3" }}>1m</span>
<span style={{ color: palette.textSecondary }}>Approved if right rail remains visible at 1280px breakpoint.</span>
</div>
</div>
</div>
</div>
</CardContent>
</Card>
@ -389,27 +388,27 @@ export default function MockupPage() {
<CardDescription style={{ color: palette.textSecondary }}>Persistent awareness while working tasks.</CardDescription>
</CardHeader>
<CardContent className="space-y-4 pt-0">
<div className={`${subPanelClass} p-3 shadow-[0_8px_20px_rgba(0,0,0,0.25)]`} style={{ borderColor: palette.border, backgroundColor: palette.mutedBg }}>
<p className="mb-2 text-sm font-semibold">Live Agents</p>
<div className="space-y-1 text-sm">
<p className="flex items-center justify-between"><span>swarm-view-integrator</span><span style={{ color: palette.success }}>online</span></p>
<p className="flex items-center justify-between"><span>social-view-integrator</span><span style={{ color: palette.warning }}>away</span></p>
<p className="flex items-center justify-between"><span>graph-integrator</span><span style={{ color: palette.info }}>busy</span></p>
<div className={`${subPanelClass} p-3 shadow-[0_8px_20px_rgba(0,0,0,0.25)]`} style={{ borderColor: palette.border, backgroundColor: palette.mutedBg }}>
<p className="mb-2 text-sm font-semibold">Live Agents</p>
<div className="space-y-1 text-sm">
<p className="flex items-center justify-between"><span>swarm-view-integrator</span><span style={{ color: palette.success }}>online</span></p>
<p className="flex items-center justify-between"><span>social-view-integrator</span><span style={{ color: palette.warning }}>away</span></p>
<p className="flex items-center justify-between"><span>graph-integrator</span><span style={{ color: palette.info }}>busy</span></p>
</div>
</div>
</div>
<div className={`${subPanelClass} p-3 shadow-[0_8px_20px_rgba(0,0,0,0.25)]`} style={{ borderColor: palette.border, backgroundColor: palette.mutedBg }}>
<p className="mb-2 text-sm font-semibold">Recent Activity</p>
<div className="space-y-1 text-xs" style={{ color: palette.textSecondary }}>
<p>5m · bb-z6s moved to in progress</p>
<p>11m · bb-atf received 2 comments</p>
<p>18m · bb-3ha marked closed</p>
<p>33m · bb-nuy dependency changed</p>
<div className={`${subPanelClass} p-3 shadow-[0_8px_20px_rgba(0,0,0,0.25)]`} style={{ borderColor: palette.border, backgroundColor: palette.mutedBg }}>
<p className="mb-2 text-sm font-semibold">Recent Activity</p>
<div className="space-y-1 text-xs" style={{ color: palette.textSecondary }}>
<p>5m · bb-z6s moved to in progress</p>
<p>11m · bb-atf received 2 comments</p>
<p>18m · bb-3ha marked closed</p>
<p>33m · bb-nuy dependency changed</p>
</div>
</div>
<div className={`${subPanelClass} p-3 shadow-[0_8px_20px_rgba(0,0,0,0.22)]`} style={{ borderColor: "#6A4E2F", backgroundColor: "#3A332B" }}>
<p className="mb-2 text-sm font-semibold">Attention</p>
<p className="flex items-center gap-2 text-sm" style={{ color: "#F2C684" }}><TriangleAlert className="h-4 w-4" /> 2 blocked tasks in selected epic</p>
</div>
</div>
<div className={`${subPanelClass} p-3 shadow-[0_8px_20px_rgba(0,0,0,0.22)]`} style={{ borderColor: "#6A4E2F", backgroundColor: "#3A332B" }}>
<p className="mb-2 text-sm font-semibold">Attention</p>
<p className="flex items-center gap-2 text-sm" style={{ color: "#F2C684" }}><TriangleAlert className="h-4 w-4" /> 2 blocked tasks in selected epic</p>
</div>
</CardContent>
</Card>
</section>

View file

@ -34,6 +34,7 @@ interface AgentRosterEntry {
interface ActivityPanelProps {
issues: BeadIssue[];
collapsed?: boolean;
projectRoot: string;
}
const AGENT_LABEL = 'gt:agent';
@ -243,7 +244,7 @@ function getInitials(name: string): string {
return name.split(/[-_\s]/).map(p => p[0]).join('').toUpperCase().slice(0, 2);
}
export function ActivityPanel({ issues, collapsed = false }: ActivityPanelProps) {
export function ActivityPanel({ issues, collapsed = false, projectRoot }: ActivityPanelProps) {
const [activities, setActivities] = useState<ActivityEvent[]>([]);
const [isLoading, setIsLoading] = useState(true);
@ -270,13 +271,16 @@ export function ActivityPanel({ issues, collapsed = false }: ActivityPanelProps)
// Subscribe to real-time activity
useEffect(() => {
const source = new EventSource('/api/events');
console.log('[ActivityPanel] Connecting to SSE for:', projectRoot);
const source = new EventSource(`/api/events?projectRoot=${encodeURIComponent(projectRoot)}`);
const onActivity = (event: MessageEvent) => {
try {
const data = JSON.parse(event.data);
if (data?.event) {
setActivities(prev => [data.event, ...prev].slice(0, 50));
console.log('[ActivityPanel] Received activity event:', data);
// data IS the activity event directly (not wrapped in { event: ... })
if (data?.beadId) {
setActivities(prev => [data, ...prev].slice(0, 50));
}
} catch (e) {
// Ignore parse errors
@ -286,10 +290,11 @@ export function ActivityPanel({ issues, collapsed = false }: ActivityPanelProps)
source.addEventListener('activity', onActivity as EventListener);
return () => {
console.log('[ActivityPanel] Closing SSE connection');
source.removeEventListener('activity', onActivity as EventListener);
source.close();
};
}, []);
}, [projectRoot]);
const activeAgents = agentRoster.filter(a => a.status === 'active').length;
if (collapsed) {

View file

@ -27,47 +27,47 @@ export interface MissionCardProps {
}
const STATUS_CONFIG = {
planning: {
color: 'text-blue-400',
border: 'border-blue-500/30',
bg: 'bg-blue-500/5',
planning: {
color: 'text-blue-400',
border: 'border-blue-500/30',
bg: 'bg-blue-500/5',
label: 'PLANNING',
icon: Circle
},
active: {
color: 'text-emerald-400',
border: 'border-emerald-500/30',
bg: 'bg-emerald-500/5',
active: {
color: 'text-emerald-400',
border: 'border-emerald-500/30',
bg: 'bg-emerald-500/5',
label: 'ACTIVE',
icon: Activity
},
blocked: {
color: 'text-rose-400',
border: 'border-rose-500/30',
bg: 'bg-rose-500/5',
blocked: {
color: 'text-rose-400',
border: 'border-rose-500/30',
bg: 'bg-rose-500/5',
label: 'BLOCKED',
icon: AlertTriangle
},
completed: {
color: 'text-slate-400',
border: 'border-slate-500/30',
bg: 'bg-slate-500/5',
completed: {
color: 'text-slate-400',
border: 'border-slate-500/30',
bg: 'bg-slate-500/5',
label: 'COMPLETE',
icon: CheckCircle2
},
};
export function MissionCard({ id, projectRoot, title, description, status, stats, agents, onDeploy, onClick }: MissionCardProps) {
export function MissionCard({ id, projectRoot, title, description, status, agents, onDeploy, onClick }: MissionCardProps) {
const config = STATUS_CONFIG[status] || STATUS_CONFIG.planning;
const StatusIcon = config.icon;
const { topology, isLoading } = useSwarmTopology(projectRoot, id);
const isUnstaffed = agents.length === 0;
const isWorking = agents.some(a => a.status === 'working');
const showPulse = status === 'active' || isWorking;
return (
<Card
<Card
onClick={onClick}
className="group relative flex flex-col h-[320px] cursor-pointer overflow-hidden rounded-2xl border border-[var(--ui-border-soft)] bg-[var(--ui-bg-card)] hover:border-[var(--ui-accent-info)] hover:shadow-xl hover:shadow-black/20 transition-all duration-300"
>
@ -106,7 +106,7 @@ export function MissionCard({ id, projectRoot, title, description, status, stats
{/* GRAPH VISUALIZATION */}
<div className="px-5 py-2 flex-1 flex flex-col justify-end">
<SwarmGraph topology={topology} isLoading={isLoading} />
<SwarmGraph topology={topology} isLoading={isLoading} />
</div>
{/* FOOTER: SQUAD */}
@ -129,14 +129,14 @@ export function MissionCard({ id, projectRoot, title, description, status, stats
)}
</div>
<Button
size="sm"
<Button
size="sm"
variant="ghost"
onClick={(e) => { e.stopPropagation(); onDeploy(); }}
className={cn(
"h-7 px-3 text-[10px] font-bold uppercase tracking-wider border transition-all",
isUnstaffed
? "border-blue-500/20 text-blue-400 bg-blue-500/5 hover:bg-blue-500/10 hover:border-blue-500/40"
isUnstaffed
? "border-blue-500/20 text-blue-400 bg-blue-500/5 hover:bg-blue-500/10 hover:border-blue-500/40"
: "border-slate-700 text-slate-400 hover:text-white hover:bg-white/5 hover:border-slate-500"
)}
>

View file

@ -1,7 +1,6 @@
'use client';
import { useMemo } from 'react';
import { motion } from 'framer-motion';
import type { SwarmTopologyData } from '../../hooks/use-swarm-topology';
interface SwarmGraphProps {
@ -12,27 +11,26 @@ interface SwarmGraphProps {
export function SwarmGraph({ topology, isLoading }: SwarmGraphProps) {
const nodes = useMemo(() => {
if (!topology) return [];
// Simple layout strategy: Clusters
// Done: Left side
// Active: Center
// Ready: Right
// Blocked: Bottom Right
const output: React.ReactNode[] = [];
const scale = 0.5;
// 1. Completed (Green Cluster)
topology.completed.forEach((item, i) => {
const col = i % 5;
const row = Math.floor(i / 5);
output.push(
<circle
<circle
key={`done-${item.id}`}
cx={20 + (col * 8)}
cy={20 + (row * 8)}
r={2.5}
fill="#34d399"
cx={20 + (col * 8)}
cy={20 + (row * 8)}
r={2.5}
fill="#34d399"
opacity={0.5}
/>
);
@ -40,32 +38,32 @@ export function SwarmGraph({ topology, isLoading }: SwarmGraphProps) {
// 2. Active (Pulsing Center)
topology.active.forEach((item, i) => {
const cx = 140 + (i * 20);
const cy = 30 + (i % 2) * 10;
output.push(
<g key={`active-${item.id}`}>
<circle cx={cx} cy={cy} r={6} fill="#10b981" className="animate-pulse" />
<circle cx={cx} cy={cy} r={3} fill="#ecfdf5" />
</g>
);
const cx = 140 + (i * 20);
const cy = 30 + (i % 2) * 10;
output.push(
<g key={`active-${item.id}`}>
<circle cx={cx} cy={cy} r={6} fill="#10b981" className="animate-pulse" />
<circle cx={cx} cy={cy} r={3} fill="#ecfdf5" />
</g>
);
});
// 3. Ready (White Pipeline)
topology.ready.forEach((item, i) => {
const cx = 220 + (i * 10);
const cy = 30;
output.push(
<circle key={`ready-${item.id}`} cx={cx} cy={cy} r={3} fill="#94a3b8" />
);
const cx = 220 + (i * 10);
const cy = 30;
output.push(
<circle key={`ready-${item.id}`} cx={cx} cy={cy} r={3} fill="#94a3b8" />
);
});
// 4. Blocked (Red Hazard)
topology.blocked.forEach((item, i) => {
const cx = 220 + (i * 10);
const cy = 50;
output.push(
<circle key={`blocked-${item.id}`} cx={cx} cy={cy} r={3} fill="#f43f5e" />
);
const cx = 220 + (i * 10);
const cy = 50;
output.push(
<circle key={`blocked-${item.id}`} cx={cx} cy={cy} r={3} fill="#f43f5e" />
);
});
return output;
@ -80,11 +78,11 @@ export function SwarmGraph({ topology, isLoading }: SwarmGraphProps) {
}
if (!topology || (topology.completed.length === 0 && topology.active.length === 0 && topology.ready.length === 0)) {
return (
<div className="h-16 w-full flex items-center justify-center bg-black/20 rounded-lg border border-dashed border-slate-800">
<span className="text-[10px] text-slate-600 font-mono">EMPTY SIGNAL</span>
</div>
);
return (
<div className="h-16 w-full flex items-center justify-center bg-black/20 rounded-lg border border-dashed border-slate-800">
<span className="text-[10px] text-slate-600 font-mono">EMPTY SIGNAL</span>
</div>
);
}
return (
@ -93,9 +91,9 @@ export function SwarmGraph({ topology, isLoading }: SwarmGraphProps) {
{/* Connection Lines (Abstract) */}
<path d="M 60 30 L 130 30" stroke="#334155" strokeWidth="1" strokeDasharray="4 4" />
<path d="M 180 30 L 210 30" stroke="#334155" strokeWidth="1" strokeDasharray="4 4" />
{nodes}
{/* Labels */}
<text x="30" y="55" fontSize="8" fill="#475569" textAnchor="middle" fontFamily="monospace">DONE</text>
<text x="150" y="55" fontSize="8" fill="#10b981" textAnchor="middle" fontFamily="monospace" fontWeight="bold">ACTIVE</text>

View file

@ -93,8 +93,6 @@ export function ThreadDrawer({
const [saveError, setSaveError] = useState<string | null>(null);
const [saveState, setSaveState] = useState<'ready' | 'saving' | 'saved' | 'error'>('ready');
const [comments, setComments] = useState<CommentFromApi[]>([]);
const [commentsLoading, setCommentsLoading] = useState(false);
// Fetch comments when drawer opens
useEffect(() => {
if (!isOpen || !id || !projectRoot) {
@ -103,7 +101,6 @@ export function ThreadDrawer({
}
const fetchComments = async () => {
setCommentsLoading(true);
try {
const response = await fetch(`/api/beads/${id}/comments?projectRoot=${encodeURIComponent(projectRoot)}`);
const payload = (await response.json()) as { ok: boolean; comments?: CommentFromApi[] };
@ -112,8 +109,6 @@ export function ThreadDrawer({
}
} catch (error) {
console.error('Failed to fetch comments:', error);
} finally {
setCommentsLoading(false);
}
};
@ -239,12 +234,12 @@ export function ThreadDrawer({
const frameShellStyle = takeover
? undefined
: {
width: embedded ? '100%' : '26rem',
background: 'linear-gradient(180deg, var(--ui-bg-card), var(--ui-bg-shell))',
borderLeft: embedded ? 'none' : '1px solid var(--color-border-default)',
boxShadow: embedded ? 'none' : '-20px 0 48px rgba(0,0,0,0.45)',
overscrollBehavior: 'contain' as const,
};
width: embedded ? '100%' : '26rem',
background: 'linear-gradient(180deg, var(--ui-bg-card), var(--ui-bg-shell))',
borderLeft: embedded ? 'none' : '1px solid var(--color-border-default)',
boxShadow: embedded ? 'none' : '-20px 0 48px rgba(0,0,0,0.45)',
overscrollBehavior: 'contain' as const,
};
const conversationSection = (
<section className="rounded-xl border border-[var(--ui-border-soft)] bg-[var(--ui-bg-shell)] p-3 shadow-[0_12px_28px_-22px_rgba(0,0,0,0.7)]">
@ -406,34 +401,34 @@ export function ThreadDrawer({
style={
isMobile
? {
paddingTop: takeover ? 'max(1rem, env(safe-area-inset-top))' : undefined,
paddingBottom: takeover ? 'max(1rem, env(safe-area-inset-bottom))' : undefined,
}
paddingTop: takeover ? 'max(1rem, env(safe-area-inset-top))' : undefined,
paddingBottom: takeover ? 'max(1rem, env(safe-area-inset-bottom))' : undefined,
}
: undefined
}
>
<div className={frameShellClass} style={frameShellStyle}>
<header className="border-b border-[var(--ui-border-soft)] bg-[var(--ui-bg-shell)] px-4 py-3">
<div className="flex items-start justify-between gap-3">
<div className="min-w-0">
<div className="mb-1 flex items-center gap-2">
<p className="font-mono text-xs font-semibold text-[var(--ui-accent-info)]">#{id}</p>
<span className="rounded-full border border-[var(--ui-accent-ready)]/45 bg-[var(--ui-accent-ready)]/20 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-[0.1em] text-[#d8ffe8]">
In Progress
</span>
<div className="flex items-start justify-between gap-3">
<div className="min-w-0">
<div className="mb-1 flex items-center gap-2">
<p className="font-mono text-xs font-semibold text-[var(--ui-accent-info)]">#{id}</p>
<span className="rounded-full border border-[var(--ui-accent-ready)]/45 bg-[var(--ui-accent-ready)]/20 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-[0.1em] text-[#d8ffe8]">
In Progress
</span>
</div>
<h2 className="truncate text-[40px] font-semibold leading-[1.12] tracking-[-0.02em] text-[var(--ui-text-primary)]" title={title}>{title}</h2>
<p className="mt-1 text-xs text-[var(--ui-text-muted)]">{threadItems.length} events</p>
</div>
<h2 className="truncate text-[40px] font-semibold leading-[1.12] tracking-[-0.02em] text-[var(--ui-text-primary)]" title={title}>{title}</h2>
<p className="mt-1 text-xs text-[var(--ui-text-muted)]">{threadItems.length} events</p>
<Button
onClick={onClose}
variant="ghost"
className="h-8 w-8 rounded-full p-0 text-[var(--ui-text-muted)] hover:bg-white/10 hover:text-[var(--ui-text-primary)]"
aria-label="Close thread"
>
<X className="h-4 w-4" />
</Button>
</div>
<Button
onClick={onClose}
variant="ghost"
className="h-8 w-8 rounded-full p-0 text-[var(--ui-text-muted)] hover:bg-white/10 hover:text-[var(--ui-text-primary)]"
aria-label="Close thread"
>
<X className="h-4 w-4" />
</Button>
</div>
</header>
<ScrollArea className="flex-1">
@ -457,11 +452,11 @@ export function ThreadDrawer({
style={
isMobile
? {
paddingBottom: 'max(0.75rem, env(safe-area-inset-bottom))',
position: 'sticky',
bottom: 0,
zIndex: 10,
}
paddingBottom: 'max(0.75rem, env(safe-area-inset-bottom))',
position: 'sticky',
bottom: 0,
zIndex: 10,
}
: undefined
}
>

View file

@ -1,6 +1,6 @@
'use client';
import { useMemo, useState } from 'react';
import { useMemo, useState, useEffect } from 'react';
import { useRouter } from 'next/navigation';
import type { BeadIssue } from '../../lib/types';
import type { ProjectScopeOption } from '../../lib/project-scope';
@ -17,6 +17,7 @@ import { SwarmMissionPicker } from '../swarm/swarm-mission-picker';
import { buildSocialCards } from '../../lib/social-cards';
import { ActivityPanel } from '../activity/activity-panel';
import { useSwarmList } from '../../hooks/use-swarm-list';
import { useBeadsSubscription } from '../../hooks/use-beads-subscription';
export interface UnifiedShellProps {
issues: BeadIssue[];
@ -27,13 +28,16 @@ export interface UnifiedShellProps {
}
export function UnifiedShell({
issues,
issues: initialIssues,
projectRoot,
projectScopeOptions,
}: UnifiedShellProps) {
const router = useRouter();
const { view, taskId, setTaskId, swarmId, setSwarmId, graphTab, setGraphTab, panel, drawer, setDrawer, epicId, setEpicId } = useUrlState();
// Subscribe to SSE for real-time updates on ALL views
const { issues, refresh } = useBeadsSubscription(initialIssues, projectRoot);
const [filters, setFilters] = useState<LeftPanelFilters>({
query: '',
status: 'all',
@ -116,6 +120,7 @@ export function UnifiedShell({
<SwarmWorkspace
selectedMissionId={swarmId ?? undefined}
issues={filteredIssues}
projectRoot={projectRoot}
/>
);
}
@ -157,7 +162,7 @@ export function UnifiedShell({
{/* RIGHT PANEL: Activity or Custom */}
<RightPanel isOpen={panel === 'open'}>
{customRightPanel || <ActivityPanel issues={issues} />}
{customRightPanel || <ActivityPanel issues={issues} projectRoot={projectRoot} />}
</RightPanel>
</div>

View file

@ -1,108 +1,262 @@
import React from 'react';
import { X, Save, ShieldAlert } from 'lucide-react';
"use client";
import React, { useState, useEffect } from 'react';
import { X, Save, ShieldAlert, Trash2, Plus } from 'lucide-react';
import type { AgentArchetype } from '../../lib/types-swarm';
interface ArchetypeInspectorProps {
archetype: AgentArchetype;
archetype?: AgentArchetype;
onClose: () => void;
onSave: (data: Partial<AgentArchetype>) => Promise<void>;
onDelete?: (id: string) => Promise<void>;
}
export function ArchetypeInspector({ archetype, onClose }: ArchetypeInspectorProps) {
if (!archetype) return null;
export function ArchetypeInspector({ archetype, onClose, onSave, onDelete }: ArchetypeInspectorProps) {
const isNew = !archetype;
const [name, setName] = useState(archetype?.name || '');
const [description, setDescription] = useState(archetype?.description || '');
const [systemPrompt, setSystemPrompt] = useState(archetype?.systemPrompt || '');
const [capabilities, setCapabilities] = useState<string[]>(archetype?.capabilities || []);
const [color, setColor] = useState(archetype?.color || '#3b82f6');
const [newCapability, setNewCapability] = useState('');
const [isSaving, setIsSaving] = useState(false);
const [isDeleting, setIsDeleting] = useState(false);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
if (archetype) {
setName(archetype.name);
setDescription(archetype.description);
setSystemPrompt(archetype.systemPrompt);
setCapabilities(archetype.capabilities);
setColor(archetype.color);
}
}, [archetype]);
const handleAddCapability = () => {
if (newCapability.trim()) {
setCapabilities([...capabilities, newCapability.trim().toLowerCase()]);
setNewCapability('');
}
};
const handleRemoveCapability = (index: number) => {
setCapabilities(capabilities.filter((_, i) => i !== index));
};
const handleSave = async () => {
if (!name.trim() || !systemPrompt.trim()) {
setError('Name and System Prompt are required');
return;
}
setIsSaving(true);
setError(null);
try {
await onSave({
id: archetype?.id,
name: name.trim(),
description: description.trim(),
systemPrompt: systemPrompt.trim(),
capabilities,
color,
isBuiltIn: archetype?.isBuiltIn
});
onClose();
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to save');
} finally {
setIsSaving(false);
}
};
const handleDelete = async () => {
if (!archetype || !onDelete) return;
if (!confirm(`Delete archetype "${archetype.name}"? This cannot be undone.`)) return;
setIsDeleting(true);
setError(null);
try {
await onDelete(archetype.id);
onClose();
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to delete');
} finally {
setIsDeleting(false);
}
};
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm p-4 animate-in fade-in duration-200">
<div className="flex flex-col h-[85vh] w-full max-w-2xl overflow-hidden rounded-xl border border-[var(--ui-border-soft)] bg-[#0f1824] shadow-2xl animate-in zoom-in-95 duration-200">
{/* Header */}
<div className="flex items-center justify-between border-b border-[var(--ui-border-soft)] px-5 py-4 bg-[#14202e]">
<div className="flex items-center gap-3">
<div
className="h-10 w-10 rounded-lg flex items-center justify-center font-bold text-lg border"
style={{ backgroundColor: `${archetype.color}15`, color: archetype.color, borderColor: `${archetype.color}30` }}
style={{ backgroundColor: `${color}15`, color: color, borderColor: `${color}30` }}
>
{archetype.name.charAt(0)}
{name.charAt(0) || '?'}
</div>
<div>
<h2 className="text-lg font-bold text-[var(--ui-text-primary)] leading-tight">{archetype.name}</h2>
<p className="font-mono uppercase tracking-wider text-[10px] text-[var(--ui-text-muted)] mt-0.5">{archetype.id}</p>
<h2 className="text-lg font-bold text-[var(--ui-text-primary)] leading-tight">
{isNew ? 'New Archetype' : name || 'Edit Archetype'}
</h2>
{!isNew && (
<p className="font-mono uppercase tracking-wider text-[10px] text-[var(--ui-text-muted)] mt-0.5">{archetype.id}</p>
)}
</div>
</div>
<button
onClick={onClose}
className="rounded-full p-2 text-[var(--ui-text-muted)] hover:bg-white/5 hover:text-white transition-colors"
>
<button onClick={onClose} className="rounded-full p-2 text-[var(--ui-text-muted)] hover:bg-white/5 hover:text-white transition-colors">
<X className="w-5 h-5" />
</button>
</div>
{/* Body Content */}
<div className="flex-1 overflow-y-auto p-5 space-y-6 custom-scrollbar">
{error && (
<div className="mx-5 mt-4 p-3 bg-rose-500/10 border border-rose-500/20 rounded-lg text-rose-400 text-sm">
{error}
</div>
)}
{/* ReadOnly Warning if builtin */}
{archetype.isBuiltIn && (
<div className="flex items-start gap-3 bg-[var(--ui-accent-warning)]/10 border border-[var(--ui-accent-warning)]/20 p-3 rounded-lg text-[var(--ui-accent-warning)]">
<ShieldAlert className="w-5 h-5 flex-shrink-0 mt-0.5" />
<div className="text-sm">
<span className="font-semibold">Built-in Archetype.</span> This is a core system role. You cannot delete it, but you can override its system prompt.
</div>
{archetype?.isBuiltIn && (
<div className="mx-5 mt-4 flex items-start gap-3 bg-[var(--ui-accent-warning)]/10 border border-[var(--ui-accent-warning)]/20 p-3 rounded-lg text-[var(--ui-accent-warning)]">
<ShieldAlert className="w-5 h-5 flex-shrink-0 mt-0.5" />
<div className="text-sm">
<span className="font-semibold">Built-in Archetype.</span> This is a core system role. You cannot delete it.
</div>
)}
</div>
)}
{/* Metadata Section */}
<div className="space-y-4">
<div>
<label className="text-xs font-semibold text-[var(--ui-text-muted)] uppercase tracking-wider mb-1.5 block">Description</label>
<div className="flex-1 overflow-y-auto p-5 space-y-6 custom-scrollbar">
<div>
<label className="text-xs font-semibold text-[var(--ui-text-muted)] uppercase tracking-wider mb-1.5 block">Name *</label>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="e.g., System Architect"
className="w-full bg-[#0a111a] border border-[var(--ui-border-soft)] rounded-md px-3 py-2 text-sm text-[var(--ui-text-primary)] focus:outline-none focus:border-[var(--ui-accent-info)] focus:ring-1 focus:ring-[var(--ui-accent-info)]"
/>
</div>
<div>
<label className="text-xs font-semibold text-[var(--ui-text-muted)] uppercase tracking-wider mb-1.5 block">Description</label>
<input
type="text"
value={description}
onChange={(e) => setDescription(e.target.value)}
placeholder="Brief description of this archetype's role"
className="w-full bg-[#0a111a] border border-[var(--ui-border-soft)] rounded-md px-3 py-2 text-sm text-[var(--ui-text-primary)] focus:outline-none focus:border-[var(--ui-accent-info)] focus:ring-1 focus:ring-[var(--ui-accent-info)]"
/>
</div>
<div>
<label className="text-xs font-semibold text-[var(--ui-text-muted)] uppercase tracking-wider mb-1.5 block">Color</label>
<div className="flex items-center gap-3">
<input
type="color"
value={color}
onChange={(e) => setColor(e.target.value)}
className="w-10 h-10 rounded cursor-pointer border border-[var(--ui-border-soft)]"
/>
<input
type="text"
defaultValue={archetype.description}
readOnly
className="w-full bg-[#0a111a] border border-[var(--ui-border-soft)] rounded-md px-3 py-2 text-sm text-[var(--ui-text-primary)] focus:outline-none focus:border-[var(--ui-accent-info)] focus:ring-1 focus:ring-[var(--ui-accent-info)]"
value={color}
onChange={(e) => setColor(e.target.value)}
className="flex-1 bg-[#0a111a] border border-[var(--ui-border-soft)] rounded-md px-3 py-2 text-sm text-[var(--ui-text-primary)] focus:outline-none focus:border-[var(--ui-accent-info)] focus:ring-1 focus:ring-[var(--ui-accent-info)]"
/>
</div>
<div>
<label className="text-xs font-semibold text-[var(--ui-text-muted)] uppercase tracking-wider mb-1.5 block">Capabilities</label>
<div className="flex flex-wrap gap-2">
{archetype.capabilities.map((cap, idx) => (
<span key={idx} className="px-2 py-1 rounded-md bg-white/5 text-[11px] uppercase font-semibold text-[var(--ui-text-muted)] border border-white/10">
{cap}
</span>
))}
</div>
</div>
</div>
<div className="border-t border-[var(--ui-border-soft)] pt-6">
<div className="flex flex-col h-[300px]">
<label className="text-xs font-semibold text-[var(--ui-text-muted)] uppercase tracking-wider mb-1.5 flex items-center justify-between">
<span>System Prompt</span>
<span className="text-[10px] text-emerald-400 normal-case tracking-normal">Syntax: Markdown</span>
</label>
<textarea
defaultValue={archetype.systemPrompt}
readOnly
className="flex-1 w-full bg-[#0a111a] border border-[var(--ui-border-soft)] rounded-md p-4 text-sm text-[var(--ui-text-primary)] font-mono resize-none focus:outline-none focus:border-[var(--ui-accent-info)] custom-scrollbar leading-relaxed"
<div>
<label className="text-xs font-semibold text-[var(--ui-text-muted)] uppercase tracking-wider mb-1.5 block">System Prompt *</label>
<textarea
value={systemPrompt}
onChange={(e) => setSystemPrompt(e.target.value)}
placeholder="You are an expert software engineer..."
rows={6}
className="w-full bg-[#0a111a] border border-[var(--ui-border-soft)] rounded-md px-3 py-2 text-sm text-[var(--ui-text-primary)] font-mono resize-y focus:outline-none focus:border-[var(--ui-accent-info)] focus:ring-1 focus:ring-[var(--ui-accent-info)] custom-scrollbar"
/>
</div>
<div>
<label className="text-xs font-semibold text-[var(--ui-text-muted)] uppercase tracking-wider mb-1.5 block">Capabilities</label>
<div className="flex gap-2 mb-3">
<input
type="text"
value={newCapability}
onChange={(e) => setNewCapability(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && (e.preventDefault(), handleAddCapability())}
placeholder="e.g., execute_code"
className="flex-1 bg-[#0a111a] border border-[var(--ui-border-soft)] rounded-md px-3 py-2 text-sm text-[var(--ui-text-primary)] focus:outline-none focus:border-[var(--ui-accent-info)] focus:ring-1 focus:ring-[var(--ui-accent-info)]"
/>
<button
type="button"
onClick={handleAddCapability}
disabled={!newCapability.trim()}
className="px-3 py-2 bg-[var(--ui-border-soft)] hover:bg-[var(--ui-border-hover)] text-white rounded-md transition-colors disabled:opacity-50 flex items-center gap-1 text-sm font-medium"
>
<Plus className="w-4 h-4" /> Add
</button>
</div>
{capabilities.length > 0 ? (
<div className="flex flex-wrap gap-2">
{capabilities.map((cap, i) => (
<div key={i} className="flex items-center gap-1.5 bg-[#14202e] border border-[var(--ui-border-soft)] px-2.5 py-1 rounded-full text-xs text-[var(--ui-text-primary)] isolate">
<div className="w-2 h-2 rounded-full" style={{ backgroundColor: color }}></div>
<span>{cap}</span>
<button
type="button"
onClick={() => handleRemoveCapability(i)}
className="ml-1 text-[var(--ui-text-muted)] hover:text-rose-400 transition-colors"
>
<X className="w-3.5 h-3.5" />
</button>
</div>
))}
</div>
) : (
<div className="text-sm text-[var(--ui-text-muted)] italic py-2">No specific capabilities defined.</div>
)}
</div>
</div>
{/* Footer Controls */}
<div className="border-t border-[var(--ui-border-soft)] px-5 py-4 bg-[#14202e] flex justify-end gap-3">
<button
onClick={onClose}
className="px-4 py-2 text-sm font-semibold text-[var(--ui-text-primary)] hover:bg-white/5 rounded-md transition-colors"
>
Cancel
</button>
<button
disabled
className="flex items-center gap-2 px-4 py-2 text-sm font-bold bg-[var(--ui-accent-info)] hover:bg-[var(--ui-accent-info)]/90 text-white rounded-md transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
<Save className="w-4 h-4" />
Save Changes
</button>
<div className="border-t border-[var(--ui-border-soft)] bg-[#0A111A] p-4 flex items-center justify-between flex-shrink-0 rounded-b-xl">
<div>
{!isNew && !archetype?.isBuiltIn && (
<button
type="button"
onClick={handleDelete}
disabled={isDeleting}
className="px-4 py-2 border border-rose-500/20 text-rose-400 hover:bg-rose-500/10 rounded-md text-sm font-medium transition-colors flex items-center gap-2 cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed"
>
<Trash2 className="w-4 h-4" />
{isDeleting ? 'Deleting...' : 'Delete'}
</button>
)}
</div>
<div className="flex justify-end gap-3">
<button
type="button"
onClick={onClose}
className="px-4 py-2 text-sm font-medium text-[var(--ui-text-muted)] hover:text-white transition-colors cursor-pointer"
>
Cancel
</button>
<button
onClick={handleSave}
disabled={isSaving}
className="px-5 py-2 bg-[var(--ui-accent-info)] hover:bg-[var(--ui-accent-info)]/90 text-white rounded-md text-sm font-medium transition-colors flex items-center gap-2 shadow-lg shadow-blue-500/20 cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed"
>
<Save className="w-4 h-4" />
{isSaving ? 'Saving...' : (isNew ? 'Create Archetype' : 'Save Changes')}
</button>
</div>
</div>
</div>
</div>

View file

@ -20,7 +20,7 @@ import {
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import { Loader2, Plus, Rocket } from 'lucide-react';
import { Loader2, Rocket } from 'lucide-react';
interface LaunchSwarmDialogProps {
projectRoot: string;
@ -51,7 +51,7 @@ export function LaunchSwarmDialog({ projectRoot, onSuccess }: LaunchSwarmDialogP
} else {
setError(json.error);
}
} catch (e) {
} catch {
setError('Failed to fetch formulas');
} finally {
setLoading(false);
@ -82,7 +82,7 @@ export function LaunchSwarmDialog({ projectRoot, onSuccess }: LaunchSwarmDialogP
proto: selectedFormula,
}),
});
const json = await res.json();
if (json.ok) {
setOpen(false);
@ -92,7 +92,7 @@ export function LaunchSwarmDialog({ projectRoot, onSuccess }: LaunchSwarmDialogP
} else {
setError(json.error);
}
} catch (e) {
} catch {
setError('Failed to launch swarm');
} finally {
setLoading(false);
@ -102,9 +102,9 @@ export function LaunchSwarmDialog({ projectRoot, onSuccess }: LaunchSwarmDialogP
return (
<Dialog open={open} onOpenChange={handleOpenChange}>
<DialogTrigger asChild>
<Button
variant="outline"
size="sm"
<Button
variant="outline"
size="sm"
className="gap-2 border-emerald-500/20 bg-emerald-500/10 text-emerald-400 hover:bg-emerald-500/20 hover:text-emerald-300"
>
<Rocket className="h-4 w-4" />
@ -127,7 +127,7 @@ export function LaunchSwarmDialog({ projectRoot, onSuccess }: LaunchSwarmDialogP
</SelectTrigger>
<SelectContent className="bg-slate-800 border-slate-700 text-slate-200">
{formulas.length === 0 && !loading && (
<div className="p-2 text-xs text-slate-500 text-center">No formulas found</div>
<div className="p-2 text-xs text-slate-500 text-center">No formulas found</div>
)}
{formulas.map((f) => (
<SelectItem key={f.name} value={f.name} className="focus:bg-slate-700 focus:text-slate-100">
@ -148,7 +148,7 @@ export function LaunchSwarmDialog({ projectRoot, onSuccess }: LaunchSwarmDialogP
disabled={loading}
/>
</div>
{error && (
<div className="text-xs text-rose-400 bg-rose-950/20 p-2 rounded border border-rose-900/30">
{error}
@ -156,9 +156,9 @@ export function LaunchSwarmDialog({ projectRoot, onSuccess }: LaunchSwarmDialogP
)}
</form>
<DialogFooter>
<Button
type="submit"
onClick={handleSubmit}
<Button
type="submit"
onClick={handleSubmit}
disabled={loading || !title || !selectedFormula}
className="bg-emerald-600 hover:bg-emerald-500 text-white"
>

View file

@ -5,7 +5,7 @@ import { Card } from '../../../components/ui/card';
import { Badge } from '../../../components/ui/badge';
import { Button } from '@/components/ui/button';
import { cn } from '../../lib/utils';
import { CheckCircle2, PlayCircle, Clock, AlertCircle, UserPlus, UserMinus, Activity } from 'lucide-react';
import { CheckCircle2, PlayCircle, Clock, AlertCircle, UserPlus, Activity } from 'lucide-react';
import { AgentAvatar } from '../shared/agent-avatar';
import { useAgentPool } from '../../hooks/use-agent-pool';
@ -13,7 +13,6 @@ interface SwarmControlCardProps {
card: SwarmCardData;
projectRoot: string;
onJoin?: () => void;
onLeave?: () => void;
isJoining?: boolean;
}
@ -21,17 +20,17 @@ function MiniGraph({ progress }: { progress: number }) {
// A simple visual indicator of progress complexity (mocked for now, but implies graph structure)
return (
<div className="flex h-8 items-end gap-0.5 opacity-50">
{[...Array(10)].map((_, i) => {
const height = Math.max(20, Math.random() * 80);
const active = (i * 10) < progress;
return (
<div
key={i}
{[...Array(10)].map((_, i) => {
const height = Math.max(20, Math.random() * 80);
const active = (i * 10) < progress;
return (
<div
key={i}
className={cn("w-1 rounded-t-sm transition-all", active ? "bg-emerald-500" : "bg-slate-700")}
style={{ height: `${active ? height : 20}%` }}
/>
)
})}
/>
)
})}
</div>
);
}
@ -42,14 +41,14 @@ const STATUS_COLORS: Record<string, string> = {
in_progress: 'text-amber-400 border-amber-400/30',
};
export function SwarmControlCard({ card, projectRoot, onJoin, onLeave, isJoining }: SwarmControlCardProps) {
export function SwarmControlCard({ card, projectRoot, onJoin, isJoining }: SwarmControlCardProps) {
const { getAgentsBySwarm } = useAgentPool(projectRoot);
const agents = getAgentsBySwarm(card.swarmId);
return (
<Card className="group relative overflow-hidden rounded-xl border border-[var(--ui-border-soft)] bg-[var(--ui-bg-card)] p-0 shadow-lg transition-all hover:border-[var(--ui-accent-info)] hover:shadow-xl">
{/* Background Decoration */}
<div className="absolute right-0 top-0 h-32 w-32 -translate-y-16 translate-x-16 rounded-full bg-emerald-500/5 blur-3xl transition-opacity group-hover:opacity-20" />
{/* Background Decoration */}
<div className="absolute right-0 top-0 h-32 w-32 -translate-y-16 translate-x-16 rounded-full bg-emerald-500/5 blur-3xl transition-opacity group-hover:opacity-20" />
<div className="flex flex-col h-full p-4 space-y-4">
{/* Header */}
@ -75,11 +74,11 @@ export function SwarmControlCard({ card, projectRoot, onJoin, onLeave, isJoining
{/* Visualizer */}
<div className="rounded-lg bg-black/20 p-2">
<div className="flex justify-between items-end mb-1">
<span className="text-[10px] text-slate-500 font-mono">ACTIVITY</span>
<span className="text-[10px] text-emerald-400 font-mono">{card.progressPercent}%</span>
</div>
<MiniGraph progress={card.progressPercent} />
<div className="flex justify-between items-end mb-1">
<span className="text-[10px] text-slate-500 font-mono">ACTIVITY</span>
<span className="text-[10px] text-emerald-400 font-mono">{card.progressPercent}%</span>
</div>
<MiniGraph progress={card.progressPercent} />
</div>
{/* Stats */}
@ -107,10 +106,10 @@ export function SwarmControlCard({ card, projectRoot, onJoin, onLeave, isJoining
<div className="flex -space-x-2">
{agents.slice(0, 3).map(agent => (
<div key={agent.agent_id} className="ring-2 ring-[var(--ui-bg-card)] rounded-full z-10">
<AgentAvatar
name={agent.display_name}
status={agent.status as any}
size="sm"
<AgentAvatar
name={agent.display_name}
status={agent.status as any}
size="sm"
/>
</div>
))}
@ -120,24 +119,24 @@ export function SwarmControlCard({ card, projectRoot, onJoin, onLeave, isJoining
</div>
)}
{agents.length === 0 && (
<span className="text-[10px] text-slate-500 italic pl-1">No agents</span>
<span className="text-[10px] text-slate-500 italic pl-1">No agents</span>
)}
</div>
<div className="flex gap-2">
<Button
size="sm"
variant="outline"
className="h-7 px-2 text-[10px] gap-1 border-emerald-500/20 hover:bg-emerald-500/10 hover:text-emerald-400"
onClick={(e) => {
e.stopPropagation();
onJoin?.();
}}
disabled={isJoining}
>
<UserPlus className="h-3 w-3" />
Join
</Button>
<Button
size="sm"
variant="outline"
className="h-7 px-2 text-[10px] gap-1 border-emerald-500/20 hover:bg-emerald-500/10 hover:text-emerald-400"
onClick={(e) => {
e.stopPropagation();
onJoin?.();
}}
disabled={isJoining}
>
<UserPlus className="h-3 w-3" />
Join
</Button>
</div>
</div>
</div>

View file

@ -1,9 +1,8 @@
'use client';
import { useEffect, useState } from 'react';
import type { SwarmCardData, SwarmStatusFromApi } from '../../lib/swarm-api';
import type { SwarmStatusFromApi } from '../../lib/swarm-api';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { CheckCircle2, PlayCircle, Clock, AlertCircle, Loader2, Users } from 'lucide-react';
import { AgentAvatar } from '../shared/agent-avatar';
import { useAgentPool } from '../../hooks/use-agent-pool';
@ -36,7 +35,7 @@ export function SwarmInspector({ swarmId, projectRoot }: SwarmInspectorProps) {
const [status, setStatus] = useState<SwarmStatusFromApi | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const { agents, getAgentsBySwarm } = useAgentPool(projectRoot);
const { getAgentsBySwarm } = useAgentPool(projectRoot);
useEffect(() => {
async function fetchStatus() {
@ -52,7 +51,7 @@ export function SwarmInspector({ swarmId, projectRoot }: SwarmInspectorProps) {
} else {
setError(payload.error?.message || 'Failed to load swarm status');
}
} catch (e) {
} catch {
setError('Failed to fetch swarm status');
} finally {
setIsLoading(false);
@ -100,7 +99,7 @@ export function SwarmInspector({ swarmId, projectRoot }: SwarmInspectorProps) {
{/* Agent Roster */}
<section>
<div className="flex items-center justify-between mb-3">
<h4 className="text-xs font-bold uppercase tracking-widest text-slate-500 flex items-center gap-2">
<h4 className="text-xs font-bold uppercase tracking-widest text-slate-500 flex items-center gap-2">
<Users className="h-3 w-3" />
Assigned Agents
</h4>
@ -108,21 +107,21 @@ export function SwarmInspector({ swarmId, projectRoot }: SwarmInspectorProps) {
{assignedAgents.length}
</span>
</div>
{assignedAgents.length === 0 ? (
<div className="text-xs text-slate-500 italic p-3 border border-dashed border-slate-800 rounded-lg text-center">
No agents currently assigned.
<br/>
<span className="text-[10px]">Use "Join" on the main card.</span>
</div>
<div className="text-xs text-slate-500 italic p-3 border border-dashed border-slate-800 rounded-lg text-center">
No agents currently assigned.
<br />
<span className="text-[10px]">Use &quot;Join&quot; on the main card.</span>
</div>
) : (
<div className="space-y-2">
{assignedAgents.map(agent => (
<div key={agent.agent_id} className="flex items-center gap-3 p-2 rounded-lg bg-slate-800/50 border border-slate-800">
<AgentAvatar
name={agent.display_name}
status={agent.status as any}
size="sm"
<AgentAvatar
name={agent.display_name}
status={agent.status as any}
size="sm"
/>
<div>
<p className="text-xs font-medium text-slate-300">{agent.display_name}</p>

View file

@ -11,15 +11,15 @@ import { useTemplates } from '../../hooks/use-templates';
import { ArchetypeInspector } from './archetype-inspector';
import { TemplateInspector } from './template-inspector';
export function SwarmWorkspace({ selectedMissionId, issues = [] }: { selectedMissionId?: string, issues?: BeadIssue[] }) {
export function SwarmWorkspace({ selectedMissionId, issues = [], projectRoot }: { selectedMissionId?: string, issues?: BeadIssue[], projectRoot: string }) {
const [activeTab, setActiveTab] = useState<'operations' | 'archetypes' | 'templates'>('operations');
// Inspector State
const [inspectingArchetypeId, setInspectingArchetypeId] = useState<string | null>(null);
const [inspectingTemplateId, setInspectingTemplateId] = useState<string | null>(null);
const { archetypes, isLoading: archetypesLoading } = useArchetypes();
const { templates, isLoading: templatesLoading } = useTemplates();
const { archetypes, isLoading: archetypesLoading, saveArchetype, deleteArchetype } = useArchetypes(projectRoot);
const { templates, isLoading: templatesLoading, saveTemplate, deleteTemplate } = useTemplates(projectRoot);
// Simulation State
const [isSimulating, setIsSimulating] = useState(false);
@ -152,7 +152,15 @@ export function SwarmWorkspace({ selectedMissionId, issues = [] }: { selectedMis
case 'archetypes':
return (
<div className="p-6 bg-[#0f1824]/30 rounded-xl border border-[var(--ui-border-soft)] h-full animate-in fade-in slide-in-from-bottom-4 duration-500 overflow-y-auto custom-scrollbar">
<h3 className="text-xl font-bold text-[var(--ui-text-primary)] mb-2">Agent Archetypes</h3>
<div className="flex items-center justify-between mb-2">
<h3 className="text-xl font-bold text-[var(--ui-text-primary)]">Agent Archetypes</h3>
<button
onClick={() => setInspectingArchetypeId('')}
className="px-3 py-1.5 bg-[var(--ui-accent-info)] hover:bg-[var(--ui-accent-info)]/90 text-white rounded-md text-xs font-semibold shadow-md transition-colors"
>
+ Create Archetype
</button>
</div>
<p className="text-[var(--ui-text-muted)] text-sm mb-6">Manage the base roles and system prompts available to your swarms.</p>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{archetypesLoading ? (
@ -207,7 +215,15 @@ export function SwarmWorkspace({ selectedMissionId, issues = [] }: { selectedMis
case 'templates':
return (
<div className="p-6 bg-[#0f1824]/30 rounded-xl border border-[var(--ui-border-soft)] h-full animate-in fade-in slide-in-from-bottom-4 duration-500 overflow-y-auto custom-scrollbar">
<h3 className="text-xl font-bold text-[var(--ui-text-primary)] mb-2">Swarm Templates</h3>
<div className="flex items-center justify-between mb-2">
<h3 className="text-xl font-bold text-[var(--ui-text-primary)]">Swarm Templates</h3>
<button
onClick={() => setInspectingTemplateId('')}
className="px-3 py-1.5 bg-[var(--ui-accent-info)] hover:bg-[var(--ui-accent-info)]/90 text-white rounded-md text-xs font-semibold shadow-md transition-colors"
>
+ Create Template
</button>
</div>
<p className="text-[var(--ui-text-muted)] text-sm mb-6">Define predefined teams and formulas for rapid mission deployment.</p>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{templatesLoading ? (
@ -324,18 +340,22 @@ export function SwarmWorkspace({ selectedMissionId, issues = [] }: { selectedMis
</main>
{/* Popups */}
{inspectingArchetypeId && (
{inspectingArchetypeId !== null && (
<ArchetypeInspector
archetype={archetypes.find(a => a.id === inspectingArchetypeId)!}
archetype={archetypes.find(a => a.id === inspectingArchetypeId)}
onClose={() => setInspectingArchetypeId(null)}
onSave={saveArchetype}
onDelete={deleteArchetype}
/>
)}
{inspectingTemplateId && (
{inspectingTemplateId !== null && (
<TemplateInspector
template={templates.find(t => t.id === inspectingTemplateId)!}
template={templates.find(t => t.id === inspectingTemplateId)}
archetypes={archetypes}
onClose={() => setInspectingTemplateId(null)}
onSave={saveTemplate}
onDelete={deleteTemplate}
/>
)}
</div>

View file

@ -1,23 +1,110 @@
import React from 'react';
import { X, Save, Edit, Link, Network } from 'lucide-react';
"use client";
import React, { useState, useEffect } from 'react';
import { X, Save, Trash2, Plus, Network, ShieldAlert } from 'lucide-react';
import type { SwarmTemplate, AgentArchetype } from '../../lib/types-swarm';
interface TemplateInspectorProps {
template: SwarmTemplate;
template?: SwarmTemplate;
archetypes: AgentArchetype[];
onClose: () => void;
onSave: (data: Partial<SwarmTemplate>) => Promise<void>;
onDelete?: (id: string) => Promise<void>;
}
export function TemplateInspector({ template, archetypes, onClose }: TemplateInspectorProps) {
if (!template) return null;
export function TemplateInspector({ template, archetypes, onClose, onSave, onDelete }: TemplateInspectorProps) {
const isNew = !template;
const totalAgents = template.team.reduce((acc, curr) => acc + curr.count, 0);
const [name, setName] = useState(template?.name || '');
const [description, setDescription] = useState(template?.description || '');
const [team, setTeam] = useState<{ archetypeId: string; count: number }[]>(template?.team || []);
const [protoFormula, setProtoFormula] = useState(template?.protoFormula || '');
const [isSaving, setIsSaving] = useState(false);
const [isDeleting, setIsDeleting] = useState(false);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
if (template) {
setName(template.name);
setDescription(template.description);
setTeam(template.team);
setProtoFormula(template.protoFormula || '');
}
}, [template]);
const updateTeamMember = (index: number, field: 'archetypeId' | 'count', value: string | number) => {
const newTeam = [...team];
if (field === 'count') {
newTeam[index] = { ...newTeam[index], count: Math.max(1, Number(value)) };
} else {
newTeam[index] = { ...newTeam[index], archetypeId: value as string };
}
setTeam(newTeam);
};
const addTeamMember = () => {
const firstAvailableArchetype = archetypes[0]?.id || '';
setTeam([...team, { archetypeId: firstAvailableArchetype, count: 1 }]);
};
const removeTeamMember = (index: number) => {
setTeam(team.filter((_, i) => i !== index));
};
const handleSave = async () => {
if (!name.trim()) {
setError('Name is required');
return;
}
if (team.length === 0) {
setError('At least one team member is required');
return;
}
setIsSaving(true);
setError(null);
try {
await onSave({
id: template?.id,
name: name.trim(),
description: description.trim(),
team,
protoFormula: protoFormula.trim() || undefined,
isBuiltIn: template?.isBuiltIn
});
onClose();
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to save');
} finally {
setIsSaving(false);
}
};
const handleDelete = async () => {
if (!template || !onDelete) return;
if (!confirm(`Delete template "${template.name}"? This cannot be undone.`)) return;
setIsDeleting(true);
setError(null);
try {
await onDelete(template.id);
onClose();
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to delete');
} finally {
setIsDeleting(false);
}
};
const totalAgents = team.reduce((acc, curr) => acc + curr.count, 0);
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm p-4 animate-in fade-in duration-200">
<div className="flex flex-col h-[75vh] w-full max-w-2xl overflow-hidden rounded-xl border border-[var(--ui-border-soft)] bg-[#0f1824] shadow-2xl animate-in zoom-in-95 duration-200">
<div className="flex flex-col h-[80vh] w-full max-w-2xl overflow-hidden rounded-xl border border-[var(--ui-border-soft)] bg-[#0f1824] shadow-2xl animate-in zoom-in-95 duration-200">
{/* Header */}
<div className="flex items-center justify-between border-b border-[var(--ui-border-soft)] px-5 py-4 bg-[#14202e]">
<div className="flex items-center gap-4">
<div className="h-10 w-10 flex-shrink-0 rounded-full bg-amber-500/10 border border-amber-500/20 flex items-center justify-center text-amber-500 font-bold text-lg">
@ -25,113 +112,159 @@ export function TemplateInspector({ template, archetypes, onClose }: TemplateIns
</div>
<div>
<div className="flex items-center gap-2">
<h2 className="text-lg font-bold text-[var(--ui-text-primary)] leading-tight">{template.name}</h2>
{template.isBuiltIn && (
<h2 className="text-lg font-bold text-[var(--ui-text-primary)] leading-tight">
{isNew ? 'New Template' : name || 'Edit Template'}
</h2>
{template?.isBuiltIn && (
<span className="px-1.5 py-0.5 rounded-full bg-white/10 text-[9px] uppercase font-bold text-[var(--ui-text-muted)] border border-white/10">Built-in</span>
)}
</div>
<p className="font-mono uppercase tracking-wider text-[10px] text-[var(--ui-text-muted)] mt-0.5">{template.id}</p>
{!isNew && (
<p className="font-mono uppercase tracking-wider text-[10px] text-[var(--ui-text-muted)] mt-0.5">{template.id}</p>
)}
</div>
</div>
<button
onClick={onClose}
className="rounded-full p-2 text-[var(--ui-text-muted)] hover:bg-white/5 hover:text-white transition-colors"
>
<button onClick={onClose} className="rounded-full p-2 text-[var(--ui-text-muted)] hover:bg-white/5 hover:text-white transition-colors">
<X className="w-5 h-5" />
</button>
</div>
{/* Body Content */}
<div className="flex-1 overflow-y-auto p-5 space-y-6 custom-scrollbar">
{error && (
<div className="mx-5 mt-4 p-3 bg-rose-500/10 border border-rose-500/20 rounded-lg text-rose-400 text-sm">
{error}
</div>
)}
{/* Metadata Section */}
{template?.isBuiltIn && (
<div className="mx-5 mt-4 flex items-start gap-3 bg-[var(--ui-accent-warning)]/10 border border-[var(--ui-accent-warning)]/20 p-3 rounded-lg text-[var(--ui-accent-warning)]">
<ShieldAlert className="w-5 h-5 flex-shrink-0 mt-0.5" />
<div className="text-sm">
<span className="font-semibold">Built-in Template.</span> This is a core system template. You cannot delete it.
</div>
</div>
)}
<div className="flex-1 overflow-y-auto p-5 space-y-6 custom-scrollbar">
<div>
<label className="text-xs font-semibold text-[var(--ui-text-muted)] uppercase tracking-wider mb-1.5 block">Purpose / Description</label>
<textarea
defaultValue={template.description}
readOnly
rows={2}
className="w-full bg-[#0a111a] border border-[var(--ui-border-soft)] rounded-md px-3 py-2 text-sm text-[var(--ui-text-primary)] focus:outline-none focus:border-[var(--ui-accent-info)] focus:ring-1 focus:ring-[var(--ui-accent-info)] resize-none"
<label className="text-xs font-semibold text-[var(--ui-text-muted)] uppercase tracking-wider mb-1.5 block">Name *</label>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="e.g., Standard Application Swarm"
className="w-full bg-[#0a111a] border border-[var(--ui-border-soft)] rounded-md px-3 py-2 text-sm text-[var(--ui-text-primary)] focus:outline-none focus:border-[var(--ui-accent-info)] focus:ring-1 focus:ring-[var(--ui-accent-info)]"
/>
</div>
<div>
<label className="text-xs font-semibold text-[var(--ui-text-muted)] uppercase tracking-wider mb-1.5 block">Description</label>
<textarea
value={description}
onChange={(e) => setDescription(e.target.value)}
rows={2}
placeholder="Describe the purpose of this swarm template..."
className="w-full bg-[#0a111a] border border-[var(--ui-border-soft)] rounded-md px-3 py-2 text-sm text-[var(--ui-text-primary)] focus:outline-none focus:border-[var(--ui-accent-info)] resize-none"
/>
</div>
{/* Team Composition Builder */}
<div className="border-t border-[var(--ui-border-soft)] pt-5">
<div className="flex items-center justify-between mb-4">
<label className="text-xs font-semibold text-[var(--ui-text-muted)] uppercase tracking-wider flex items-center gap-2">
<Network className="w-4 h-4 text-emerald-500" />
Roster Composition
</label>
<button className="text-[11px] font-semibold text-[var(--ui-accent-info)] hover:text-white bg-[var(--ui-accent-info)]/10 px-2 py-1 rounded transition-colors disabled:opacity-50">
+ Add Member
<label className="text-xs font-semibold text-[var(--ui-text-muted)] uppercase tracking-wider block">Team Composition *</label>
<button
type="button"
onClick={addTeamMember}
className="px-2 py-1 bg-[var(--ui-border-soft)] hover:bg-[var(--ui-border-hover)] text-white rounded transition-colors flex items-center gap-1 text-xs font-medium"
>
<Plus className="w-3.5 h-3.5" /> Add Member
</button>
</div>
<div className="space-y-2">
{template.team.map((member, idx) => {
const arch = archetypes.find(a => a.id === member.archetypeId);
return (
<div key={idx} className="flex items-center gap-3 bg-[#111f2b] border border-[var(--ui-border-soft)] p-3 rounded-lg">
<div className="h-8 w-8 rounded text-sm flex items-center justify-center font-bold" style={{ backgroundColor: `${arch?.color || '#888'}20`, color: arch?.color || '#888' }}>
{arch?.name.charAt(0) || '?'}
</div>
<div className="flex-1">
<div className="font-semibold text-sm text-[var(--ui-text-primary)]">{arch?.name || member.archetypeId}</div>
<div className="text-[11px] text-[var(--ui-text-muted)]">{arch?.description || 'Unknown Archetype'}</div>
</div>
<div className="flex items-center gap-2 bg-[#0a111a] border border-[var(--ui-border-soft)] rounded-md p-1">
<span className="text-xs font-mono text-[var(--ui-text-muted)] px-2">Count:</span>
<input
type="number"
defaultValue={member.count}
readOnly
className="w-12 bg-transparent text-sm font-bold text-center text-[var(--ui-text-primary)] focus:outline-none"
/>
</div>
</div>
);
})}
<div className="space-y-3">
{team.map((member, index) => (
<div key={index} className="flex items-center gap-2">
<select
value={member.archetypeId}
onChange={(e) => updateTeamMember(index, 'archetypeId', e.target.value)}
className="flex-1 bg-[#0a111a] border border-[var(--ui-border-soft)] rounded-md px-3 py-2 text-sm text-[var(--ui-text-primary)] focus:outline-none focus:border-[var(--ui-accent-info)]"
>
{archetypes.map(a => (
<option key={a.id} value={a.id}>{a.name}</option>
))}
</select>
<input
type="number"
min="1"
value={member.count}
onChange={(e) => updateTeamMember(index, 'count', e.target.value)}
className="w-20 bg-[#0a111a] border border-[var(--ui-border-soft)] rounded-md px-3 py-2 text-sm text-[var(--ui-text-primary)] focus:outline-none focus:border-[var(--ui-accent-info)]"
/>
<button
type="button"
onClick={() => removeTeamMember(index)}
className="p-2 text-[var(--ui-text-muted)] hover:text-rose-400 hover:bg-white/5 rounded-md transition-colors"
>
<Trash2 className="w-4 h-4" />
</button>
</div>
))}
{team.length === 0 && (
<div className="text-sm text-[var(--ui-text-muted)] italic py-4 text-center border border-dashed border-[var(--ui-border-soft)] rounded-md">
No agents assigned. Add a member to build your team.
</div>
)}
</div>
</div>
{/* Advanced: Proto-formula */}
<div className="border-t border-[var(--ui-border-soft)] pt-5">
<label className="text-xs font-semibold text-[var(--ui-text-muted)] uppercase tracking-wider mb-2 flex items-center gap-2">
<Link className="w-4 h-4 text-amber-500" />
MOL Proto-Formula (Optional)
<div>
<label className="text-xs font-semibold text-[var(--ui-text-muted)] uppercase tracking-wider mb-1.5 block flex items-center gap-1.5">
<Network className="w-3 h-3" /> Proto-Formula (Optional)
</label>
<div className="flex items-center gap-3">
<input
type="text"
defaultValue={template.protoFormula || ''}
placeholder="e.g. 'release' or 'bugfix'"
readOnly
className="flex-1 bg-[#0a111a] border border-[var(--ui-border-soft)] rounded-md px-3 py-2 font-mono text-sm text-amber-500 focus:outline-none focus:border-amber-500/50 focus:ring-1 focus:ring-amber-500/50"
/>
<div className="text-[11px] text-[var(--ui-text-muted)] max-w-[200px] leading-tight">
Specifies a Gastown Formula to execute (`bd mol pour`) when launching this swarm.
</div>
</div>
<textarea
value={protoFormula}
onChange={(e) => setProtoFormula(e.target.value)}
rows={3}
placeholder="Optional default interaction rules or steps..."
className="w-full bg-[#0a111a] border border-[var(--ui-border-soft)] rounded-md px-3 py-2 text-sm font-mono text-[var(--ui-text-primary)] focus:outline-none focus:border-[var(--ui-accent-info)] resize-y custom-scrollbar"
/>
</div>
</div>
{/* Footer Controls */}
<div className="border-t border-[var(--ui-border-soft)] px-5 py-4 bg-[#14202e] flex justify-end gap-3">
<button
onClick={onClose}
className="px-4 py-2 text-sm font-semibold text-[var(--ui-text-primary)] hover:bg-white/5 rounded-md transition-colors"
>
Close
</button>
<button
disabled
className="flex items-center gap-2 px-4 py-2 text-sm font-bold bg-[var(--ui-accent-info)] hover:bg-[var(--ui-accent-info)]/90 text-white rounded-md transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
<Save className="w-4 h-4" />
Save Template
</button>
<div className="border-t border-[var(--ui-border-soft)] bg-[#0A111A] p-4 flex items-center justify-between flex-shrink-0 rounded-b-xl">
<div>
{!isNew && !template?.isBuiltIn && (
<button
type="button"
onClick={handleDelete}
disabled={isDeleting}
className="px-4 py-2 border border-rose-500/20 text-rose-400 hover:bg-rose-500/10 rounded-md text-sm font-medium transition-colors flex items-center gap-2 cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed"
>
<Trash2 className="w-4 h-4" />
{isDeleting ? 'Deleting...' : 'Delete'}
</button>
)}
</div>
<div className="flex justify-end gap-3">
<button
type="button"
onClick={onClose}
className="px-4 py-2 text-sm font-medium text-[var(--ui-text-muted)] hover:text-white transition-colors cursor-pointer"
>
Cancel
</button>
<button
onClick={handleSave}
disabled={isSaving}
className="px-5 py-2 bg-[var(--ui-accent-info)] hover:bg-[var(--ui-accent-info)]/90 text-white rounded-md text-sm font-medium transition-colors flex items-center gap-2 shadow-lg shadow-blue-500/20 cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed"
>
<Save className="w-4 h-4" />
{isSaving ? 'Saving...' : (isNew ? 'Create Template' : 'Save Changes')}
</button>
</div>
</div>
</div>
</div>
);

View file

@ -13,14 +13,14 @@ interface UseAgentPoolResult {
async function fetchAgents(projectRoot: string): Promise<AgentRecord[]> {
try {
const response = await fetch(`/api/agents/list?projectRoot=${encodeURIComponent(projectRoot)}`);
const response = await fetch(`/api/agents/list?projectRoot=${encodeURIComponent(projectRoot)}`, { cache: 'no-store' });
const payload = await response.json();
if (!response.ok || !payload.ok) {
console.error('Agent fetch failed:', payload.error);
return [];
}
return (payload.data || []) as AgentRecord[];
} catch (err) {
console.error('Agent fetch error:', err);

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