checkpoint: pre-split branch cleanup

This commit is contained in:
ZenchantLive 2026-03-03 16:43:42 -08:00
parent 4c2ae2e5b7
commit b5db7a7753
276 changed files with 35912 additions and 60119 deletions

1
.agent/skills/shadcn-ui Symbolic link
View file

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

File diff suppressed because it is too large Load diff

View file

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

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

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

1
.augment/skills/shadcn-ui Symbolic link
View file

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

File diff suppressed because it is too large Load diff

View file

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

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

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

3
.beads/agent.txt Normal file
View file

@ -0,0 +1,3 @@
✓ Created issue: beadboard-8bs — Agent: Antigravity Graph Orchestrator
Priority: P0
Status: open

61
.beads/create_help.txt Normal file
View file

@ -0,0 +1,61 @@
Create a new issue (or multiple issues from markdown file)
Usage:
bd create [title] [flags]
Aliases:
create, new
Flags:
--acceptance string Acceptance criteria
--agent-rig string Agent's rig name (requires --type=agent)
--append-notes string Append to existing notes (with newline separator)
-a, --assignee string Assignee
--body-file string Read description from file (use - for stdin)
--defer string Defer until date (issue hidden from bd ready until then). Same formats as --due
--deps strings Dependencies in format 'type:id' or 'id' (e.g., 'discovered-from:bd-20,blocks:bd-15' or 'bd-20')
-d, --description string Issue description
--design string Design notes
--dry-run Preview what would be created without actually creating
--due string Due date/time. Formats: +6h, +1d, +2w, tomorrow, next monday, 2025-01-15
--ephemeral Create as ephemeral (short-lived, subject to TTL compaction)
-e, --estimate int Time estimate in minutes (e.g., 60 for 1 hour)
--event-actor string Entity URI who caused this event (requires --type=event)
--event-category string Event category (e.g., patrol.muted, agent.started) (requires --type=event)
--event-payload string Event-specific JSON data (requires --type=event)
--event-target string Entity URI or bead ID affected (requires --type=event)
--external-ref string External reference (e.g., 'gh-9', 'jira-ABC')
-f, --file string Create multiple issues from markdown file
--force Force creation even if prefix doesn't match database prefix
-h, --help help for create
--id string Explicit issue ID (e.g., 'bd-42' for partitioning)
-l, --labels strings Labels (comma-separated)
--metadata string Set custom metadata (JSON string or @file.json to read from file)
--mol-type string Molecule type: swarm (multi-polecat), patrol (recurring ops), work (default)
--no-inherit-labels Don't inherit labels from parent issue
--notes string Additional notes
--parent string Parent issue ID for hierarchical child (e.g., 'bd-a3f8e9')
--prefix string Create issue in rig by prefix (e.g., --prefix bd- or --prefix bd or --prefix beads)
-p, --priority string Priority (0-4 or P0-P4, 0=highest) (default "2")
--repo string Target repository for issue (overrides auto-routing)
--rig string Create issue in a different rig (e.g., --rig beads)
--silent Output only the issue ID (for scripting)
--spec-id string Link to specification document
--title string Issue title (alternative to positional argument)
-t, --type string Issue type (bug|feature|task|epic|chore|decision); custom types require types.custom config; aliases: enhancement/feat→feature, dec/adr→decision (default "task")
--validate Validate description contains required sections for issue type
--waits-for string Spawner issue ID to wait for (creates waits-for dependency for fanout gate)
--waits-for-gate string Gate type: all-children (wait for all) or any-children (wait for first) (default "all-children")
--wisp-type string Wisp type for TTL-based compaction: heartbeat, ping, patrol, gc_report, recovery, error, escalation
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 auto-commit policy (off|on|batch). 'on': commit after each write. 'batch': defer commits to bd sync / bd dolt commit; uncommitted changes persist in the working set until then. SIGTERM/SIGHUP flush pending batch commits. Default: off. Override via config key dolt.auto-commit
--json Output in JSON format
--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 auto-sync
-v, --verbose Enable verbose/debug output

1
.beads/debug.txt Normal file
View file

@ -0,0 +1 @@
✓ Updated issue: beadboard-txj.2 — Apply Status Colors and Transitive Context to Graph Edges

42
.beads/dep_help.txt Normal file
View file

@ -0,0 +1,42 @@
Manage dependencies between issues.
When called with an issue ID and --blocks flag, creates a blocking dependency:
bd dep <blocker-id> --blocks <blocked-id>
This is equivalent to:
bd dep add <blocked-id> <blocker-id>
Examples:
bd dep bd-xyz --blocks bd-abc # bd-xyz blocks bd-abc
bd dep add bd-abc bd-xyz # Same as above (bd-abc depends on bd-xyz)
Usage:
bd dep [issue-id] [flags]
bd dep [command]
Available Commands:
add Add a dependency
cycles Detect dependency cycles
list List dependencies or dependents of an issue
relate Create a bidirectional relates_to link between issues
remove Remove a dependency
tree Show dependency tree
unrelate Remove a relates_to link between issues
Flags:
-b, --blocks string Issue ID that this issue blocks (shorthand for: bd dep add <blocked> <blocker>)
-h, --help help for dep
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 auto-commit policy (off|on|batch). 'on': commit after each write. 'batch': defer commits to bd sync / bd dolt commit; uncommitted changes persist in the working set until then. SIGTERM/SIGHUP flush pending batch commits. Default: off. Override via config key dolt.auto-commit
--json Output in JSON format
--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 auto-sync
-v, --verbose Enable verbose/debug output
Use "bd dep [command] --help" for more information about a command.

View file

@ -1 +1 @@
3840
1910

View file

@ -1 +0,0 @@
1772407425

File diff suppressed because it is too large Load diff

View file

@ -1 +0,0 @@
60816

View file

@ -1 +0,0 @@
3307

3
.beads/epic_create.txt Normal file
View file

@ -0,0 +1,3 @@
✓ Created issue: beadboard-txj — Epic: Enhanced Graph Edge Visualization
Priority: P0
Status: open

60
.beads/lint_output.txt Normal file
View file

@ -0,0 +1,60 @@
> beadboard@0.1.0 lint
> eslint .
C:\Users\Zenchant\codex\beadboard\.beads\fix.js
1:12 error A `require()` style import is forbidden @typescript-eslint/no-require-imports
2:14 error A `require()` style import is forbidden @typescript-eslint/no-require-imports
C:\Users\Zenchant\codex\beadboard\.beads\fix2.js
1:12 error A `require()` style import is forbidden @typescript-eslint/no-require-imports
2:14 error A `require()` style import is forbidden @typescript-eslint/no-require-imports
C:\Users\Zenchant\codex\beadboard\src\components\activity\activity-panel.tsx
285:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars
C:\Users\Zenchant\codex\beadboard\src\components\activity\swarm-command-feed.tsx
83:22 warning 'e' is defined but never used @typescript-eslint/no-unused-vars
C:\Users\Zenchant\codex\beadboard\src\components\graph\dependency-graph-page.tsx
641:17 warning 'unused_' is assigned a value but never used @typescript-eslint/no-unused-vars
C:\Users\Zenchant\codex\beadboard\src\components\graph\smart-dag.tsx
38:15 warning 'hideClosedProp' is assigned a value but never used @typescript-eslint/no-unused-vars
56:45 warning '_id' is defined but never used @typescript-eslint/no-unused-vars
62:44 warning '_id' is defined but never used @typescript-eslint/no-unused-vars
84:5 warning 'signalById' is assigned a value but never used @typescript-eslint/no-unused-vars
85:5 warning 'cycleNodeIdSet' is assigned a value but never used @typescript-eslint/no-unused-vars
87:5 warning 'blockerTooltipMap' is assigned a value but never used @typescript-eslint/no-unused-vars
170:53 warning 'shouldOpenDrawer' is defined but never used @typescript-eslint/no-unused-vars
176:9 warning 'selectedIssue' is assigned a value but never used @typescript-eslint/no-unused-vars
C:\Users\Zenchant\codex\beadboard\src\components\shared\top-bar.tsx
93:82 warning 'toggleBlockedOnly' is assigned a value but never used @typescript-eslint/no-unused-vars
C:\Users\Zenchant\codex\beadboard\src\components\shared\unified-shell.tsx
42:55 warning 'panel' is assigned a value but never used @typescript-eslint/no-unused-vars
C:\Users\Zenchant\codex\beadboard\src\components\shared\workflow-graph.tsx
251:17 warning 'unused_' is assigned a value but never used @typescript-eslint/no-unused-vars
C:\Users\Zenchant\codex\beadboard\src\components\social\social-card.tsx
116:3 warning 'onOpenThread' is defined but never used @typescript-eslint/no-unused-vars
C:\Users\Zenchant\codex\beadboard\src\components\swarm\swarm-detail.tsx
4:15 warning 'SwarmCardData' is defined but never used @typescript-eslint/no-unused-vars
6:10 warning 'cn' is defined but never used @typescript-eslint/no-unused-vars
53:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars
C:\Users\Zenchant\codex\beadboard\src\components\swarm\swarm-page.tsx
18:53 warning 'LayoutGrid' is defined but never used @typescript-eslint/no-unused-vars
C:\Users\Zenchant\codex\beadboard\src\hooks\use-mission-graph.ts
33:16 warning '_e' is defined but never used @typescript-eslint/no-unused-vars
C:\Users\Zenchant\codex\beadboard\src\hooks\use-swarm-topology.ts
35:16 warning '_err' is defined but never used @typescript-eslint/no-unused-vars
Γ£û 25 problems (4 errors, 21 warnings)

123
.beads/mem.txt Normal file
View file

@ -0,0 +1,123 @@
✓ beadboard-116 · [MEMORY][WORKFLOW][HARD] Evidence before completion claims [● P1 · CLOSED]
Owner: ZenchantLive · Type: decision
Created: 2026-03-02 · Updated: 2026-03-02
Close reason: Ratified canonical memory
DESCRIPTION
Scope: All new implementation tasks that change behavior or data flow.
Out of Scope: Purely informational discussions without code or workflow changes.
Rule: Start from explicit verification evidence and update work state in bd before claiming progress complete.
Rationale: Prevents status drift and false completion claims.
Failure Mode: Unverified completion claims lead to regressions and mistrust.
NOTES
Provenance linked: bb-92d.6, beadboard-jgy, beadboard-yz6
ACCEPTANCE CRITERIA
Given an implementation task, when work begins and ends, then bd state transitions and verification command evidence are recorded.
Verification command(s): bd show <task-id>; npm run typecheck; npm run lint; npm run test
LABELS: mem-canonical, mem-hard, memory, memory-workflow
METADATA
domain: memory-workflow
effective_date: 2026-03-02
evidence_ids: bb-92d.6,beadboard-jgy,beadboard-yz6
memory_strength: hard
memory_version: 1
owner: team
plan_refs: docs/plans/2026-03-01-beads-native-memory.md,docs/plans/2026-03-02-bd-memory-fabric-design.md
superseded_by: null
supersedes: null
RELATED
↔ ✓ bb-92d.6: Add guardrail test preventing direct writes to .beads/issues.jsonl ● P0
↔ ✓ beadboard-jgy: Document memory workflow in help/memory and AGENTS manuals ● P1
↔ ○ beadboard-nq9: (EPIC) [MEMORY-ANCHOR] Workflow Protocol ● P1
↔ ✓ beadboard-yz6: Bootstrap Phase 1 Memory Fabric (anchors + canonical nodes) ● P1
────────────────────────────────────────────────────────────
✓ beadboard-60a · [MEMORY][ARCH][HARD] Dependencies model execution order, not visual order [● P1 · CLOSED]
Owner: ZenchantLive · Type: decision
Created: 2026-03-02 · Updated: 2026-03-02
Close reason: Ratified canonical memory
DESCRIPTION
Scope: Dependency graph design and updates.
Out of Scope: Visual layout decisions that do not alter execution semantics.
Rule: Dependencies encode execution order and blocking semantics, never visual grouping.
Rationale: Keeps ready/blocked states truthful and machine-reliable.
Failure Mode: Incorrect dependency direction causes false blocking or unsafe parallelism.
NOTES
Provenance linked: bb-bvn, beadboard-r1i, beadboard-68k
ACCEPTANCE CRITERIA
Given a dependency update, when graph state is queried, then blocked/ready outcomes match intended execution order.
Verification command(s): bd dep tree <issue-id>; bd blocked; bd ready
LABELS: mem-canonical, mem-hard, memory, memory-arch
METADATA
domain: memory-arch
effective_date: 2026-03-02
evidence_ids: bb-bvn,beadboard-r1i,beadboard-68k
memory_strength: hard
memory_version: 1
owner: team
plan_refs: docs/plans/2026-02-22-dag-views-ux-design.md
superseded_by: null
supersedes: null
RELATED
↔ ✓ bb-bvn: Dependency Graph (React Flow) ● P0
↔ ✓ beadboard-68k: Phase 0: UX Wiring Fixes ● P0
↔ ○ beadboard-76p: (EPIC) [MEMORY-ANCHOR] Architecture ● P1
↔ ✓ beadboard-r1i: Phase 1: Contextual Right Panel ● P0
────────────────────────────────────────────────────────────
✓ beadboard-zas · [MEMORY][ARCH][HARD] Shared logic for cross-view behavior [● P1 · CLOSED]
Owner: ZenchantLive · Type: decision
Created: 2026-03-02 · Updated: 2026-03-02
Close reason: Ratified canonical memory
DESCRIPTION
Scope: Repeated logic used by multiple views or pages.
Out of Scope: One-off prototypes and disposable experiments.
Rule: Reuse shared paths/components for cross-view behavior; avoid one-off logic drift.
Rationale: Prevents silent divergence between Kanban, Graph, and API views.
Failure Mode: Patching one surface only creates inconsistent user behavior.
NOTES
Provenance linked: beadboard-68k.4, beadboard-68k.5, beadboard-r1i.1
ACCEPTANCE CRITERIA
Given a behavior change touching multiple views, when implementation lands, then shared logic path is reused and covered by tests.
Verification command(s): rg "<shared-function-or-hook>" src tests; npm run test
LABELS: mem-canonical, mem-hard, memory, memory-arch
METADATA
domain: memory-arch
effective_date: 2026-03-02
evidence_ids: beadboard-68k.4,beadboard-68k.5,beadboard-r1i.1
memory_strength: hard
memory_version: 1
owner: team
plan_refs: docs/plans/2026-02-22-dag-views-ux-design.md,docs/plans/2026-02-15-unified-ux-prd.md
superseded_by: null
supersedes: null
RELATED
↔ ✓ beadboard-68k.4: Fix thread drawer status badge (hardcoded 'In Progress') ● P0
↔ ✓ beadboard-68k.5: Wire TopBar metric tiles from live issue data ● P0
↔ ○ beadboard-76p: (EPIC) [MEMORY-ANCHOR] Architecture ● P1
↔ ✓ beadboard-r1i.1: Extend ContextualRightPanel props and thread taskId/swarmId from shell ● P0

794
.beads/npm_test_output.txt Normal file
View file

@ -0,0 +1,794 @@
> beadboard@0.1.0 test
> node --test tests/bootstrap.test.mjs && node --import tsx --test tests/components/shared/base-card.test.tsx && node --import tsx --test tests/components/shared/agent-avatar.test.tsx && node --import tsx --test tests/components/sessions/sessions-header.test.ts && node --import tsx --test tests/components/sessions/agent-station-logic.test.ts && node --import tsx --test tests/lib/parser.test.ts && node --import tsx --test tests/lib/pathing.test.ts && node --import tsx --test tests/components/shared/left-panel.test.tsx && node --import tsx --test tests/components/shared/top-bar.test.tsx && node --import tsx --test tests/components/shared/mobile-nav.test.tsx && node --import tsx --test tests/components/swarm/swarm-card.test.tsx && node --import tsx --test tests/hooks/url-state-integration.test.ts && node --import tsx --test tests/hooks/use-graph-analysis.test.ts && node --import tsx --test tests/components/graph/smart-dag.test.tsx && node --import tsx --test tests/components/unified-shell.test.tsx && node --import tsx --test tests/components/blocked-triage-modal.test.tsx && node --import tsx --test tests/components/graph/graph-node-labels.test.tsx && node --import tsx --test tests/components/graph/graph-node-assign.test.tsx && node --import tsx --test tests/components/graph/graph-node-conversation.test.tsx && node --import tsx --test tests/lib/coord-schema.test.ts && node --import tsx --test tests/lib/install-manifest.test.ts && node --import tsx --test tests/lib/coord-events.test.ts && node --import tsx --test tests/api/coord-events-route.test.ts && node --import tsx --test tests/lib/coord-projections-inbox.test.ts && node --import tsx --test tests/lib/coord-projections-reservations.test.ts && node --import tsx --test tests/components/sessions/conversation-drawer-coord.test.tsx && node --import tsx --test tests/scripts/beadboard-launcher.test.ts && node --import tsx --test tests/scripts/install-wrappers-contract.test.ts && node --import tsx --test tests/scripts/install-sh-smoke.test.ts && node --import tsx --test tests/scripts/installer-ci-contract.test.ts && node --import tsx --test tests/docs/installer-quickstart-contract.test.ts && node --import tsx --test tests/skills/beadboard-driver/resolve-bb.test.ts && node --import tsx --test tests/skills/beadboard-driver/session-preflight.test.ts && node --import tsx --test tests/skills/beadboard-driver/generate-agent-name.test.ts && node --import tsx --test tests/skills/beadboard-driver/readiness-report.test.ts && node --import tsx --test tests/skills/beadboard-driver/skill-local-runner.test.ts && node --import tsx --test tests/skills/beadboard-driver/diagnose-env.test.ts && node --import tsx --test tests/skills/beadboard-driver/heal-common-issues.test.ts && node --import tsx --test tests/lib/epic-graph.test.ts && node --import tsx --test tests/components/shared/left-panel-filtering.test.ts && node --import tsx --test tests/hooks/use-beads-subscription-contract.test.ts && node --import tsx --test tests/components/graph/dependency-graph-hide-closed-contract.test.ts && node --import tsx --test tests/components/shared/unified-shell-hide-closed-contract.test.ts
TAP version 13
# Subtest: bootstrap scaffold files exist
ok 1 - bootstrap scaffold files exist
---
duration_ms: 1.5408
...
# Subtest: package.json has next/react/typescript scripts and deps
ok 2 - package.json has next/react/typescript scripts and deps
---
duration_ms: 0.6082
...
1..2
# tests 2
# suites 0
# pass 2
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 89.6484
TAP version 13
# Subtest: BaseCard Component Contract
# Subtest: exports BaseCard component
ok 1 - exports BaseCard component
---
duration_ms: 48.4903
...
1..1
ok 1 - BaseCard Component Contract
---
duration_ms: 49.1763
type: 'suite'
...
# Subtest: BaseCard Styling Logic
# Subtest: should be possible to import the component
ok 1 - should be possible to import the component
---
duration_ms: 1.6433
...
# Subtest: applies correct status border class for "ready" status
ok 2 - applies correct status border class for "ready" status
---
duration_ms: 8.9211
...
# Subtest: applies correct status border class for "blocked" status
ok 3 - applies correct status border class for "blocked" status
---
duration_ms: 1.9781
...
# Subtest: applies selection ring when selected prop is true
ok 4 - applies selection ring when selected prop is true
---
duration_ms: 1.3742
...
1..4
ok 2 - BaseCard Styling Logic
---
duration_ms: 14.3343
type: 'suite'
...
1..2
# tests 5
# suites 2
# pass 5
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 359.7963
TAP version 13
# Subtest: AgentAvatar Component Contract
# Subtest: exports AgentAvatar component
ok 1 - exports AgentAvatar component
---
duration_ms: 112.1114
...
1..1
ok 1 - AgentAvatar Component Contract
---
duration_ms: 112.7409
type: 'suite'
...
# Subtest: AgentAvatar Role Styling
# Subtest: applies correct role color class for "ui" role
ok 1 - applies correct role color class for "ui" role
---
duration_ms: 7.7314
...
# Subtest: applies correct role color class for "orchestrator" role
ok 2 - applies correct role color class for "orchestrator" role
---
duration_ms: 2.3245
...
1..2
ok 2 - AgentAvatar Role Styling
---
duration_ms: 10.3257
type: 'suite'
...
# Subtest: AgentAvatar ZFC States
# Subtest: applies working pulse glow
ok 1 - applies working pulse glow
---
duration_ms: 1.7868
...
1..1
ok 3 - AgentAvatar ZFC States
---
duration_ms: 2.1043
type: 'suite'
...
1..3
# tests 4
# suites 3
# pass 4
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 414.8522
TAP version 13
# Subtest: SessionsHeader: Agent Grouping
# Subtest: groups agents by swarm
ok 1 - groups agents by swarm
---
duration_ms: 2.028
...
# Subtest: shows fallback bucket for unassigned agents
ok 2 - shows fallback bucket for unassigned agents
---
duration_ms: 0.163
...
# Subtest: handles empty swarm groups
ok 3 - handles empty swarm groups
---
duration_ms: 0.1271
...
1..3
ok 1 - SessionsHeader: Agent Grouping
---
duration_ms: 3.2566
type: 'suite'
...
1..1
# tests 3
# suites 1
# pass 3
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 259.5842
TAP version 13
# Subtest: getAgentRoleColor returns correct color for known roles
ok 1 - getAgentRoleColor returns correct color for known roles
---
duration_ms: 0.7893
...
# Subtest: getAgentRoleColor returns default for unknown role
ok 2 - getAgentRoleColor returns default for unknown role
---
duration_ms: 0.1416
...
1..2
# tests 2
# suites 0
# pass 2
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 260.6046
TAP version 13
# Subtest: parseIssuesJsonl applies defaults and preserves priority 0
ok 1 - parseIssuesJsonl applies defaults and preserves priority 0
---
duration_ms: 1.1468
...
# Subtest: parseIssuesJsonl skips malformed and blank lines
ok 2 - parseIssuesJsonl skips malformed and blank lines
---
duration_ms: 0.2118
...
# Subtest: parseIssuesJsonl filters tombstones by default
ok 3 - parseIssuesJsonl filters tombstones by default
---
duration_ms: 0.1758
...
# Subtest: parseIssuesJsonl can include tombstones when requested
ok 4 - parseIssuesJsonl can include tombstones when requested
---
duration_ms: 0.1626
...
# Subtest: parseIssuesJsonl supports beads dependency schema with depends_on_id and parent-child
ok 5 - parseIssuesJsonl supports beads dependency schema with depends_on_id and parent-child
---
duration_ms: 0.7949
...
1..5
# tests 5
# suites 0
# pass 5
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 269.9367
TAP version 13
# Subtest: canonicalizeWindowsPath normalizes separators and drive casing
ok 1 - canonicalizeWindowsPath normalizes separators and drive casing
---
duration_ms: 1.4342
...
# Subtest: windowsPathKey is case-insensitive stable key
ok 2 - windowsPathKey is case-insensitive stable key
---
duration_ms: 0.4737
...
# Subtest: toDisplayPath renders forward slashes for UI readability
ok 3 - toDisplayPath renders forward slashes for UI readability
---
duration_ms: 0.2287
...
# Subtest: sameWindowsPath handles case/separator differences
ok 4 - sameWindowsPath handles case/separator differences
---
duration_ms: 0.2269
...
1..4
# tests 4
# suites 0
# pass 4
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 290.6242
TAP version 13
# Subtest: LeftPanel Component Contract
# Subtest: exports LeftPanel component
ok 1 - exports LeftPanel component
---
duration_ms: 291.6468
...
# Subtest: LeftPanel accepts issues and onEpicSelect props
ok 2 - LeftPanel accepts issues and onEpicSelect props
---
duration_ms: 1.3553
...
1..2
ok 1 - LeftPanel Component Contract
---
duration_ms: 294.0487
type: 'suite'
...
# Subtest: LeftPanel Tree Structure
# Subtest: renders epics as expandable tree items
ok 1 - renders epics as expandable tree items
---
duration_ms: 1.1796
...
# Subtest: groups beads under their parent epic
ok 2 - groups beads under their parent epic
---
duration_ms: 1.1357
...
1..2
ok 2 - LeftPanel Tree Structure
---
duration_ms: 2.5592
type: 'suite'
...
# Subtest: LeftPanel Responsive Behavior
# Subtest: applies responsive classes for desktop, tablet, and mobile
ok 1 - applies responsive classes for desktop, tablet, and mobile
---
duration_ms: 1.4154
...
1..1
ok 3 - LeftPanel Responsive Behavior
---
duration_ms: 1.6082
type: 'suite'
...
# Subtest: LeftPanel Scope Controls
# Subtest: renders scope section
ok 1 - renders scope section
---
duration_ms: 1.2414
...
1..1
ok 4 - LeftPanel Scope Controls
---
duration_ms: 1.5705
type: 'suite'
...
1..4
# tests 6
# suites 4
# pass 6
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 586.4632
TAP version 13
# Subtest: TopBar Component Contract
# Subtest: exports TopBar component
ok 1 - exports TopBar component
---
duration_ms: 576.3556
...
# Subtest: TopBar component can be imported without errors
ok 2 - TopBar component can be imported without errors
---
duration_ms: 1.4049
...
1..2
ok 1 - TopBar Component Contract
---
duration_ms: 578.6933
type: 'suite'
...
# Subtest: TopBar View Tabs
# Subtest: renders view tabs: Social, Graph
ok 1 - renders view tabs: Social, Graph
---
duration_ms: 1.1653
...
# Subtest: active tab has bold text and accent underline
ok 2 - active tab has bold text and accent underline
---
duration_ms: 1.0756
...
1..2
ok 2 - TopBar View Tabs
---
duration_ms: 2.4564
type: 'suite'
...
# Subtest: TopBar Filter and Controls
# Subtest: renders filter/search input placeholder
ok 1 - renders filter/search input placeholder
---
duration_ms: 1.2466
...
# Subtest: renders settings placeholder
ok 2 - renders settings placeholder
---
duration_ms: 1.0997
...
1..2
ok 3 - TopBar Filter and Controls
---
duration_ms: 2.7634
type: 'suite'
...
1..3
# tests 6
# suites 3
# pass 6
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 843.1609
TAP version 13
# Subtest: Mobile Navigation - Hamburger Menu
# Subtest: exports MobileNav component
ok 1 - exports MobileNav component
---
duration_ms: 122.7061
...
# Subtest: renders tab buttons: Social, Graph
ok 2 - renders tab buttons: Social, Graph
---
duration_ms: 1.5081
...
# Subtest: highlights active tab with accent color
ok 3 - highlights active tab with accent color
---
duration_ms: 1.5626
...
# Subtest: uses setView from useUrlState on tab click
ok 4 - uses setView from useUrlState on tab click
---
duration_ms: 1.3467
...
1..4
ok 1 - Mobile Navigation - Hamburger Menu
---
duration_ms: 128.9482
type: 'suite'
...
# Subtest: TopBar Hamburger Menu
# Subtest: shows hamburger button on mobile and tablet
ok 1 - shows hamburger button on mobile and tablet
---
duration_ms: 487.0038
...
# Subtest: hamburger button opens left panel drawer
ok 2 - hamburger button opens left panel drawer
---
duration_ms: 1.478
...
# Subtest: hides hamburger on desktop
ok 3 - hides hamburger on desktop
---
duration_ms: 1.1051
...
1..3
ok 2 - TopBar Hamburger Menu
---
duration_ms: 490.1177
type: 'suite'
...
1..2
# tests 7
# suites 2
# pass 7
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 899.3239
TAP version 13
# Subtest: SwarmCard Component Contract
# Subtest: exports SwarmCard component
ok 1 - exports SwarmCard component
---
duration_ms: 238.5797
...
# Subtest: SwarmCard component can be imported without errors
ok 2 - SwarmCard component can be imported without errors
---
duration_ms: 1.6861
...
1..2
ok 1 - SwarmCard Component Contract
---
duration_ms: 241.2828
type: 'suite'
...
# Subtest: SwarmCard Agent Roster
# Subtest: renders agent avatars with liveness glow
ok 1 - renders agent avatars with liveness glow
---
duration_ms: 1.7451
...
# Subtest: displays agent current task when available
ok 2 - displays agent current task when available
---
duration_ms: 1.4825
...
1..2
ok 2 - SwarmCard Agent Roster
---
duration_ms: 3.5718
type: 'suite'
...
# Subtest: SwarmCard Progress Bar
# Subtest: renders progress bar showing completion percentage
ok 1 - renders progress bar showing completion percentage
---
duration_ms: 1.6795
...
1..1
ok 3 - SwarmCard Progress Bar
---
duration_ms: 1.8984
type: 'suite'
...
# Subtest: SwarmCard Attention Items
# Subtest: renders attention items with warning styling
ok 1 - renders attention items with warning styling
---
duration_ms: 1.7077
...
1..1
ok 4 - SwarmCard Attention Items
---
duration_ms: 2.1567
type: 'suite'
...
# Subtest: SwarmCard View-Jump Icons
# Subtest: renders view-jump icons for navigation
ok 1 - renders view-jump icons for navigation
---
duration_ms: 1.7431
...
1..1
ok 5 - SwarmCard View-Jump Icons
---
duration_ms: 2.0339
type: 'suite'
...
1..5
# tests 7
# suites 5
# pass 7
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 515.754
TAP version 13
# Subtest: URL State Integration - bb-ui2.22
# Subtest: Valid URL Patterns - Social View
# Subtest: /?view=social - defaults to social view
ok 1 - /?view=social - defaults to social view
---
duration_ms: 0.8379
...
# Subtest: /?view=social&task=bb-buff.1&panel=open - task selected, panel open
ok 2 - /?view=social&task=bb-buff.1&panel=open - task selected, panel open
---
duration_ms: 0.2068
...
# Subtest: /?view=social&task=bb-ui2.22 - task with dots in ID
ok 3 - /?view=social&task=bb-ui2.22 - task with dots in ID
---
duration_ms: 0.1241
...
1..3
ok 1 - Valid URL Patterns - Social View
---
duration_ms: 1.803
type: 'suite'
...
# Subtest: Valid URL Patterns - Graph View
# Subtest: /?view=graph - graph view default
not ok 1 - /?view=graph - graph view default
---
duration_ms: 3.2799
location: 'C:\\Users\\Zenchant\\codex\\beadboard\\tests\\hooks\\url-state-integration.test.ts:2:2578'
failureType: 'testCodeFailure'
error: |-
Expected values to be strictly equal:
'overview' !== 'flow'
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected: 'flow'
actual: 'overview'
operator: 'strictEqual'
stack: |-
TestContext.<anonymous> (C:\Users\Zenchant\codex\beadboard\tests\hooks\url-state-integration.test.ts:53:14)
Test.runInAsyncScope (node:async_hooks:211:14)
Test.run (node:internal/test_runner/test:934:25)
Test.start (node:internal/test_runner/test:833:17)
node:internal/test_runner/test:1318:71
node:internal/per_context/primordials:483:82
new Promise (<anonymous>)
new SafePromise (node:internal/per_context/primordials:451:29)
node:internal/per_context/primordials:483:9
Array.map (<anonymous>)
...
# Subtest: /?view=graph&task=bb-buff.1 - graph with task selected
ok 2 - /?view=graph&task=bb-buff.1 - graph with task selected
---
duration_ms: 0.1241
...
# Subtest: /?view=graph&graphTab=flow - flow tab selected
ok 3 - /?view=graph&graphTab=flow - flow tab selected
---
duration_ms: 0.2827
...
# Subtest: /?view=graph&graphTab=overview - overview tab selected
ok 4 - /?view=graph&graphTab=overview - overview tab selected
---
duration_ms: 0.1152
...
# Subtest: /?view=graph&swarm=bb-buff - graph filtered by swarm
ok 5 - /?view=graph&swarm=bb-buff - graph filtered by swarm
---
duration_ms: 0.3114
...
1..5
not ok 2 - Valid URL Patterns - Graph View
---
duration_ms: 4.4826
type: 'suite'
location: 'C:\\Users\\Zenchant\\codex\\beadboard\\tests\\hooks\\url-state-integration.test.ts:2:2515'
failureType: 'subtestsFailed'
error: '1 subtest failed'
code: 'ERR_TEST_FAILURE'
...
# Subtest: Deprecated Swarm View Fallback
# Subtest: /?view=swarm - falls back to social (swarm view deprecated)
ok 1 - /?view=swarm - falls back to social (swarm view deprecated)
---
duration_ms: 0.176
...
# Subtest: /?view=swarm&swarm=bb-buff - falls back to social but preserves swarmId
ok 2 - /?view=swarm&swarm=bb-buff - falls back to social but preserves swarmId
---
duration_ms: 0.3375
...
# Subtest: /?view=swarm&swarm=bb-buff&panel=open - falls back to social with panel open
ok 3 - /?view=swarm&swarm=bb-buff&panel=open - falls back to social with panel open
---
duration_ms: 0.1203
...
1..3
ok 3 - Deprecated Swarm View Fallback
---
duration_ms: 0.8018
type: 'suite'
...
# Subtest: Valid URL Patterns - Activity View
# Subtest: /?view=activity - activity view default
ok 1 - /?view=activity - activity view default
---
duration_ms: 0.1469
...
# Subtest: /?view=activity&agent=bb-silver-castle - filtered by agent
ok 2 - /?view=activity&agent=bb-silver-castle - filtered by agent
---
duration_ms: 0.0856
...
# Subtest: /?view=activity&swarm=bb-buff - filtered by swarm
ok 3 - /?view=activity&swarm=bb-buff - filtered by swarm
---
duration_ms: 0.0813
...
1..3
ok 4 - Valid URL Patterns - Activity View
---
duration_ms: 0.4565
type: 'suite'
...
# Subtest: Invalid Param Handling
# Subtest: /?view=invalid - invalid view defaults to social
ok 1 - /?view=invalid - invalid view defaults to social
---
duration_ms: 0.1489
...
# Subtest: /?view=graph&graphTab=invalid - invalid graphTab defaults to flow
not ok 2 - /?view=graph&graphTab=invalid - invalid graphTab defaults to flow
---
duration_ms: 0.4762
location: 'C:\\Users\\Zenchant\\codex\\beadboard\\tests\\hooks\\url-state-integration.test.ts:2:6479'
failureType: 'testCodeFailure'
error: |-
Expected values to be strictly equal:
'overview' !== 'flow'
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected: 'flow'
actual: 'overview'
operator: 'strictEqual'
stack: |-
TestContext.<anonymous> (C:\Users\Zenchant\codex\beadboard\tests\hooks\url-state-integration.test.ts:138:14)
Test.runInAsyncScope (node:async_hooks:211:14)
Test.run (node:internal/test_runner/test:934:25)
Suite.processPendingSubtests (node:internal/test_runner/test:633:18)
Test.postRun (node:internal/test_runner/test:1045:19)
Test.run (node:internal/test_runner/test:973:12)
async Promise.all (index 0)
async Suite.run (node:internal/test_runner/test:1320:7)
async Suite.processPendingSubtests (node:internal/test_runner/test:633:7)
...
# Subtest: /?panel=invalid - invalid panel defaults to open
ok 3 - /?panel=invalid - invalid panel defaults to open
---
duration_ms: 0.1006
...
# Subtest: /?task=invalid-id - invalid task ID still parsed (no validation)
ok 4 - /?task=invalid-id - invalid task ID still parsed (no validation)
---
duration_ms: 0.0841
...
1..4
not ok 5 - Invalid Param Handling
---
duration_ms: 0.9701
type: 'suite'
location: 'C:\\Users\\Zenchant\\codex\\beadboard\\tests\\hooks\\url-state-integration.test.ts:2:6178'
failureType: 'subtestsFailed'
error: '1 subtest failed'
code: 'ERR_TEST_FAILURE'
...
# Subtest: URL Building - State to URL
# Subtest: builds social view URL
ok 1 - builds social view URL
---
duration_ms: 0.2194
...
# Subtest: builds graph view with task URL
ok 2 - builds graph view with task URL
---
duration_ms: 0.0932
...
# Subtest: builds swarm view with swarm param
ok 3 - builds swarm view with swarm param
---
duration_ms: 0.085
...
# Subtest: builds activity view with agent filter
ok 4 - builds activity view with agent filter
---
duration_ms: 0.1405
...
# Subtest: preserves existing params when adding new ones
ok 5 - preserves existing params when adding new ones
---
duration_ms: 0.1868
...
# Subtest: removes params when set to null
ok 6 - removes params when set to null
---
duration_ms: 0.1154
...
# Subtest: returns root when all params cleared
ok 7 - returns root when all params cleared
---
duration_ms: 0.0926
...
1..7
ok 6 - URL Building - State to URL
---
duration_ms: 1.1103
type: 'suite'
...
# Subtest: Complex URL Scenarios
# Subtest: handles all params together
ok 1 - handles all params together
---
duration_ms: 0.123
...
# Subtest: empty string values treated as null/empty
ok 2 - empty string values treated as null/empty
---
duration_ms: 0.0893
...
1..2
ok 7 - Complex URL Scenarios
---
duration_ms: 0.351
type: 'suite'
...
# Subtest: Deep Link Patterns - From Card Icons
# Subtest: SocialCard Graph icon: /?view=graph&task={id}
ok 1 - SocialCard Graph icon: /?view=graph&task={id}
---
duration_ms: 0.1287
...
# Subtest: SwarmCard Graph icon: /?view=graph&swarm={id}
ok 2 - SwarmCard Graph icon: /?view=graph&swarm={id}
---
duration_ms: 0.0785
...
# Subtest: SwarmCard Timeline icon: /?view=activity&swarm={id}
ok 3 - SwarmCard Timeline icon: /?view=activity&swarm={id}
---
duration_ms: 0.1411
...
# Subtest: Agent avatar click: /?view=activity&agent={id}
ok 4 - Agent avatar click: /?view=activity&agent={id}
---
duration_ms: 0.076
...
1..4
ok 8 - Deep Link Patterns - From Card Icons
---
duration_ms: 0.5678
type: 'suite'
...
1..8
not ok 1 - URL State Integration - bb-ui2.22
---
duration_ms: 11.4418
type: 'suite'
location: 'C:\\Users\\Zenchant\\codex\\beadboard\\tests\\hooks\\url-state-integration.test.ts:2:1269'
failureType: 'subtestsFailed'
error: '2 subtests failed'
code: 'ERR_TEST_FAILURE'
...
1..1
# tests 31
# suites 9
# pass 29
# fail 2
# cancelled 0
# skipped 0
# todo 0
# duration_ms 447.3716

919
.beads/npm_test_output2.txt Normal file
View file

@ -0,0 +1,919 @@
> beadboard@0.1.0 test
> node --test tests/bootstrap.test.mjs && node --import tsx --test tests/components/shared/base-card.test.tsx && node --import tsx --test tests/components/shared/agent-avatar.test.tsx && node --import tsx --test tests/components/sessions/sessions-header.test.ts && node --import tsx --test tests/components/sessions/agent-station-logic.test.ts && node --import tsx --test tests/lib/parser.test.ts && node --import tsx --test tests/lib/pathing.test.ts && node --import tsx --test tests/components/shared/left-panel.test.tsx && node --import tsx --test tests/components/shared/top-bar.test.tsx && node --import tsx --test tests/components/shared/mobile-nav.test.tsx && node --import tsx --test tests/components/swarm/swarm-card.test.tsx && node --import tsx --test tests/hooks/url-state-integration.test.ts && node --import tsx --test tests/hooks/use-graph-analysis.test.ts && node --import tsx --test tests/components/graph/smart-dag.test.tsx && node --import tsx --test tests/components/unified-shell.test.tsx && node --import tsx --test tests/components/blocked-triage-modal.test.tsx && node --import tsx --test tests/components/graph/graph-node-labels.test.tsx && node --import tsx --test tests/components/graph/graph-node-assign.test.tsx && node --import tsx --test tests/components/graph/graph-node-conversation.test.tsx && node --import tsx --test tests/lib/coord-schema.test.ts && node --import tsx --test tests/lib/install-manifest.test.ts && node --import tsx --test tests/lib/coord-events.test.ts && node --import tsx --test tests/api/coord-events-route.test.ts && node --import tsx --test tests/lib/coord-projections-inbox.test.ts && node --import tsx --test tests/lib/coord-projections-reservations.test.ts && node --import tsx --test tests/components/sessions/conversation-drawer-coord.test.tsx && node --import tsx --test tests/scripts/beadboard-launcher.test.ts && node --import tsx --test tests/scripts/install-wrappers-contract.test.ts && node --import tsx --test tests/scripts/install-sh-smoke.test.ts && node --import tsx --test tests/scripts/installer-ci-contract.test.ts && node --import tsx --test tests/docs/installer-quickstart-contract.test.ts && node --import tsx --test tests/skills/beadboard-driver/resolve-bb.test.ts && node --import tsx --test tests/skills/beadboard-driver/session-preflight.test.ts && node --import tsx --test tests/skills/beadboard-driver/generate-agent-name.test.ts && node --import tsx --test tests/skills/beadboard-driver/readiness-report.test.ts && node --import tsx --test tests/skills/beadboard-driver/skill-local-runner.test.ts && node --import tsx --test tests/skills/beadboard-driver/diagnose-env.test.ts && node --import tsx --test tests/skills/beadboard-driver/heal-common-issues.test.ts && node --import tsx --test tests/lib/epic-graph.test.ts && node --import tsx --test tests/components/shared/left-panel-filtering.test.ts && node --import tsx --test tests/hooks/use-beads-subscription-contract.test.ts && node --import tsx --test tests/components/graph/dependency-graph-hide-closed-contract.test.ts && node --import tsx --test tests/components/shared/unified-shell-hide-closed-contract.test.ts
TAP version 13
# Subtest: bootstrap scaffold files exist
ok 1 - bootstrap scaffold files exist
---
duration_ms: 2.2085
...
# Subtest: package.json has next/react/typescript scripts and deps
ok 2 - package.json has next/react/typescript scripts and deps
---
duration_ms: 0.5581
...
1..2
# tests 2
# suites 0
# pass 2
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 140.1608
TAP version 13
# Subtest: BaseCard Component Contract
# Subtest: exports BaseCard component
ok 1 - exports BaseCard component
---
duration_ms: 58.211
...
1..1
ok 1 - BaseCard Component Contract
---
duration_ms: 58.9965
type: 'suite'
...
# Subtest: BaseCard Styling Logic
# Subtest: should be possible to import the component
ok 1 - should be possible to import the component
---
duration_ms: 2.4528
...
# Subtest: applies correct status border class for "ready" status
ok 2 - applies correct status border class for "ready" status
---
duration_ms: 10.367
...
# Subtest: applies correct status border class for "blocked" status
ok 3 - applies correct status border class for "blocked" status
---
duration_ms: 2.0545
...
# Subtest: applies selection ring when selected prop is true
ok 4 - applies selection ring when selected prop is true
---
duration_ms: 1.3688
...
1..4
ok 2 - BaseCard Styling Logic
---
duration_ms: 16.6551
type: 'suite'
...
1..2
# tests 5
# suites 2
# pass 5
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 447.2767
TAP version 13
# Subtest: AgentAvatar Component Contract
# Subtest: exports AgentAvatar component
ok 1 - exports AgentAvatar component
---
duration_ms: 154.6347
...
1..1
ok 1 - AgentAvatar Component Contract
---
duration_ms: 155.6009
type: 'suite'
...
# Subtest: AgentAvatar Role Styling
# Subtest: applies correct role color class for "ui" role
ok 1 - applies correct role color class for "ui" role
---
duration_ms: 11.7114
...
# Subtest: applies correct role color class for "orchestrator" role
ok 2 - applies correct role color class for "orchestrator" role
---
duration_ms: 2.6704
...
1..2
ok 2 - AgentAvatar Role Styling
---
duration_ms: 14.7057
type: 'suite'
...
# Subtest: AgentAvatar ZFC States
# Subtest: applies working pulse glow
ok 1 - applies working pulse glow
---
duration_ms: 2.4504
...
1..1
ok 3 - AgentAvatar ZFC States
---
duration_ms: 2.8198
type: 'suite'
...
1..3
# tests 4
# suites 3
# pass 4
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 539.4517
TAP version 13
# Subtest: SessionsHeader: Agent Grouping
# Subtest: groups agents by swarm
ok 1 - groups agents by swarm
---
duration_ms: 2.114
...
# Subtest: shows fallback bucket for unassigned agents
ok 2 - shows fallback bucket for unassigned agents
---
duration_ms: 0.1808
...
# Subtest: handles empty swarm groups
ok 3 - handles empty swarm groups
---
duration_ms: 0.1339
...
1..3
ok 1 - SessionsHeader: Agent Grouping
---
duration_ms: 3.4059
type: 'suite'
...
1..1
# tests 3
# suites 1
# pass 3
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 283.1807
TAP version 13
# Subtest: getAgentRoleColor returns correct color for known roles
ok 1 - getAgentRoleColor returns correct color for known roles
---
duration_ms: 0.8966
...
# Subtest: getAgentRoleColor returns default for unknown role
ok 2 - getAgentRoleColor returns default for unknown role
---
duration_ms: 0.1598
...
1..2
# tests 2
# suites 0
# pass 2
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 309.895
TAP version 13
# Subtest: parseIssuesJsonl applies defaults and preserves priority 0
ok 1 - parseIssuesJsonl applies defaults and preserves priority 0
---
duration_ms: 1.1101
...
# Subtest: parseIssuesJsonl skips malformed and blank lines
ok 2 - parseIssuesJsonl skips malformed and blank lines
---
duration_ms: 0.1784
...
# Subtest: parseIssuesJsonl filters tombstones by default
ok 3 - parseIssuesJsonl filters tombstones by default
---
duration_ms: 0.1475
...
# Subtest: parseIssuesJsonl can include tombstones when requested
ok 4 - parseIssuesJsonl can include tombstones when requested
---
duration_ms: 0.1428
...
# Subtest: parseIssuesJsonl supports beads dependency schema with depends_on_id and parent-child
ok 5 - parseIssuesJsonl supports beads dependency schema with depends_on_id and parent-child
---
duration_ms: 0.7034
...
1..5
# tests 5
# suites 0
# pass 5
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 279.9641
TAP version 13
# Subtest: canonicalizeWindowsPath normalizes separators and drive casing
ok 1 - canonicalizeWindowsPath normalizes separators and drive casing
---
duration_ms: 0.9528
...
# Subtest: windowsPathKey is case-insensitive stable key
ok 2 - windowsPathKey is case-insensitive stable key
---
duration_ms: 0.2036
...
# Subtest: toDisplayPath renders forward slashes for UI readability
ok 3 - toDisplayPath renders forward slashes for UI readability
---
duration_ms: 0.1337
...
# Subtest: sameWindowsPath handles case/separator differences
ok 4 - sameWindowsPath handles case/separator differences
---
duration_ms: 0.1696
...
1..4
# tests 4
# suites 0
# pass 4
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 300.62
TAP version 13
# Subtest: LeftPanel Component Contract
# Subtest: exports LeftPanel component
ok 1 - exports LeftPanel component
---
duration_ms: 306.6047
...
# Subtest: LeftPanel accepts issues and onEpicSelect props
ok 2 - LeftPanel accepts issues and onEpicSelect props
---
duration_ms: 1.3879
...
1..2
ok 1 - LeftPanel Component Contract
---
duration_ms: 309.0712
type: 'suite'
...
# Subtest: LeftPanel Tree Structure
# Subtest: renders epics as expandable tree items
ok 1 - renders epics as expandable tree items
---
duration_ms: 1.345
...
# Subtest: groups beads under their parent epic
ok 2 - groups beads under their parent epic
---
duration_ms: 1.7164
...
1..2
ok 2 - LeftPanel Tree Structure
---
duration_ms: 3.332
type: 'suite'
...
# Subtest: LeftPanel Responsive Behavior
# Subtest: applies responsive classes for desktop, tablet, and mobile
ok 1 - applies responsive classes for desktop, tablet, and mobile
---
duration_ms: 1.3508
...
1..1
ok 3 - LeftPanel Responsive Behavior
---
duration_ms: 1.5231
type: 'suite'
...
# Subtest: LeftPanel Scope Controls
# Subtest: renders scope section
ok 1 - renders scope section
---
duration_ms: 1.3167
...
1..1
ok 4 - LeftPanel Scope Controls
---
duration_ms: 1.7059
type: 'suite'
...
1..4
# tests 6
# suites 4
# pass 6
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 598.9805
TAP version 13
# Subtest: TopBar Component Contract
# Subtest: exports TopBar component
ok 1 - exports TopBar component
---
duration_ms: 683.336
...
# Subtest: TopBar component can be imported without errors
ok 2 - TopBar component can be imported without errors
---
duration_ms: 1.848
...
1..2
ok 1 - TopBar Component Contract
---
duration_ms: 686.5443
type: 'suite'
...
# Subtest: TopBar View Tabs
# Subtest: renders view tabs: Social, Graph
ok 1 - renders view tabs: Social, Graph
---
duration_ms: 1.4361
...
# Subtest: active tab has bold text and accent underline
ok 2 - active tab has bold text and accent underline
---
duration_ms: 1.6179
...
1..2
ok 2 - TopBar View Tabs
---
duration_ms: 3.3478
type: 'suite'
...
# Subtest: TopBar Filter and Controls
# Subtest: renders filter/search input placeholder
ok 1 - renders filter/search input placeholder
---
duration_ms: 1.7411
...
# Subtest: renders settings placeholder
ok 2 - renders settings placeholder
---
duration_ms: 1.2955
...
1..2
ok 3 - TopBar Filter and Controls
---
duration_ms: 3.5576
type: 'suite'
...
1..3
# tests 6
# suites 3
# pass 6
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 991.5175
TAP version 13
# Subtest: Mobile Navigation - Hamburger Menu
# Subtest: exports MobileNav component
ok 1 - exports MobileNav component
---
duration_ms: 160.2862
...
# Subtest: renders tab buttons: Social, Graph
ok 2 - renders tab buttons: Social, Graph
---
duration_ms: 1.5794
...
# Subtest: highlights active tab with accent color
ok 3 - highlights active tab with accent color
---
duration_ms: 1.2026
...
# Subtest: uses setView from useUrlState on tab click
ok 4 - uses setView from useUrlState on tab click
---
duration_ms: 1.1548
...
1..4
ok 1 - Mobile Navigation - Hamburger Menu
---
duration_ms: 166.2933
type: 'suite'
...
# Subtest: TopBar Hamburger Menu
# Subtest: shows hamburger button on mobile and tablet
ok 1 - shows hamburger button on mobile and tablet
---
duration_ms: 520.9119
...
# Subtest: hamburger button opens left panel drawer
ok 2 - hamburger button opens left panel drawer
---
duration_ms: 1.9043
...
# Subtest: hides hamburger on desktop
ok 3 - hides hamburger on desktop
---
duration_ms: 1.566
...
1..3
ok 2 - TopBar Hamburger Menu
---
duration_ms: 525.0916
type: 'suite'
...
1..2
# tests 7
# suites 2
# pass 7
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 1031.056
TAP version 13
# Subtest: SwarmCard Component Contract
# Subtest: exports SwarmCard component
ok 1 - exports SwarmCard component
---
duration_ms: 263.9758
...
# Subtest: SwarmCard component can be imported without errors
ok 2 - SwarmCard component can be imported without errors
---
duration_ms: 1.4629
...
1..2
ok 1 - SwarmCard Component Contract
---
duration_ms: 266.6715
type: 'suite'
...
# Subtest: SwarmCard Agent Roster
# Subtest: renders agent avatars with liveness glow
ok 1 - renders agent avatars with liveness glow
---
duration_ms: 1.3692
...
# Subtest: displays agent current task when available
ok 2 - displays agent current task when available
---
duration_ms: 1.5995
...
1..2
ok 2 - SwarmCard Agent Roster
---
duration_ms: 3.3144
type: 'suite'
...
# Subtest: SwarmCard Progress Bar
# Subtest: renders progress bar showing completion percentage
ok 1 - renders progress bar showing completion percentage
---
duration_ms: 1.8139
...
1..1
ok 3 - SwarmCard Progress Bar
---
duration_ms: 2.0726
type: 'suite'
...
# Subtest: SwarmCard Attention Items
# Subtest: renders attention items with warning styling
ok 1 - renders attention items with warning styling
---
duration_ms: 1.8572
...
1..1
ok 4 - SwarmCard Attention Items
---
duration_ms: 2.3816
type: 'suite'
...
# Subtest: SwarmCard View-Jump Icons
# Subtest: renders view-jump icons for navigation
ok 1 - renders view-jump icons for navigation
---
duration_ms: 1.7362
...
1..1
ok 5 - SwarmCard View-Jump Icons
---
duration_ms: 1.9907
type: 'suite'
...
1..5
# tests 7
# suites 5
# pass 7
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 586.4929
TAP version 13
# Subtest: URL State Integration - bb-ui2.22
# Subtest: Valid URL Patterns - Social View
# Subtest: /?view=social - defaults to social view
ok 1 - /?view=social - defaults to social view
---
duration_ms: 0.8523
...
# Subtest: /?view=social&task=bb-buff.1&panel=open - task selected, panel open
ok 2 - /?view=social&task=bb-buff.1&panel=open - task selected, panel open
---
duration_ms: 0.2088
...
# Subtest: /?view=social&task=bb-ui2.22 - task with dots in ID
ok 3 - /?view=social&task=bb-ui2.22 - task with dots in ID
---
duration_ms: 0.1511
...
1..3
ok 1 - Valid URL Patterns - Social View
---
duration_ms: 1.865
type: 'suite'
...
# Subtest: Valid URL Patterns - Graph View
# Subtest: /?view=graph - graph view default
ok 1 - /?view=graph - graph view default
---
duration_ms: 0.2128
...
# Subtest: /?view=graph&task=bb-buff.1 - graph with task selected
ok 2 - /?view=graph&task=bb-buff.1 - graph with task selected
---
duration_ms: 0.1196
...
# Subtest: /?view=graph&graphTab=flow - flow tab selected
ok 3 - /?view=graph&graphTab=flow - flow tab selected
---
duration_ms: 0.1573
...
# Subtest: /?view=graph&graphTab=overview - overview tab selected
ok 4 - /?view=graph&graphTab=overview - overview tab selected
---
duration_ms: 0.116
...
# Subtest: /?view=graph&swarm=bb-buff - graph filtered by swarm
ok 5 - /?view=graph&swarm=bb-buff - graph filtered by swarm
---
duration_ms: 0.2221
...
1..5
ok 2 - Valid URL Patterns - Graph View
---
duration_ms: 1.1579
type: 'suite'
...
# Subtest: Deprecated Swarm View Fallback
# Subtest: /?view=swarm - falls back to social (swarm view deprecated)
ok 1 - /?view=swarm - falls back to social (swarm view deprecated)
---
duration_ms: 0.7522
...
# Subtest: /?view=swarm&swarm=bb-buff - falls back to social but preserves swarmId
ok 2 - /?view=swarm&swarm=bb-buff - falls back to social but preserves swarmId
---
duration_ms: 0.2523
...
# Subtest: /?view=swarm&swarm=bb-buff&panel=open - falls back to social with panel open
ok 3 - /?view=swarm&swarm=bb-buff&panel=open - falls back to social with panel open
---
duration_ms: 0.1228
...
1..3
ok 3 - Deprecated Swarm View Fallback
---
duration_ms: 1.276
type: 'suite'
...
# Subtest: Valid URL Patterns - Activity View
# Subtest: /?view=activity - activity view default
ok 1 - /?view=activity - activity view default
---
duration_ms: 0.1366
...
# Subtest: /?view=activity&agent=bb-silver-castle - filtered by agent
ok 2 - /?view=activity&agent=bb-silver-castle - filtered by agent
---
duration_ms: 0.0823
...
# Subtest: /?view=activity&swarm=bb-buff - filtered by swarm
ok 3 - /?view=activity&swarm=bb-buff - filtered by swarm
---
duration_ms: 0.0788
...
1..3
ok 4 - Valid URL Patterns - Activity View
---
duration_ms: 0.4389
type: 'suite'
...
# Subtest: Invalid Param Handling
# Subtest: /?view=invalid - invalid view defaults to social
ok 1 - /?view=invalid - invalid view defaults to social
---
duration_ms: 0.1141
...
# Subtest: /?view=graph&graphTab=invalid - invalid graphTab defaults to flow
ok 2 - /?view=graph&graphTab=invalid - invalid graphTab defaults to flow
---
duration_ms: 0.0807
...
# Subtest: /?panel=invalid - invalid panel defaults to open
ok 3 - /?panel=invalid - invalid panel defaults to open
---
duration_ms: 0.0801
...
# Subtest: /?task=invalid-id - invalid task ID still parsed (no validation)
ok 4 - /?task=invalid-id - invalid task ID still parsed (no validation)
---
duration_ms: 0.086
...
1..4
ok 5 - Invalid Param Handling
---
duration_ms: 0.4767
type: 'suite'
...
# Subtest: URL Building - State to URL
# Subtest: builds social view URL
ok 1 - builds social view URL
---
duration_ms: 0.2155
...
# Subtest: builds graph view with task URL
ok 2 - builds graph view with task URL
---
duration_ms: 0.1448
...
# Subtest: builds swarm view with swarm param
ok 3 - builds swarm view with swarm param
---
duration_ms: 0.0849
...
# Subtest: builds activity view with agent filter
ok 4 - builds activity view with agent filter
---
duration_ms: 0.0913
...
# Subtest: preserves existing params when adding new ones
ok 5 - preserves existing params when adding new ones
---
duration_ms: 0.1507
...
# Subtest: removes params when set to null
ok 6 - removes params when set to null
---
duration_ms: 0.1207
...
# Subtest: returns root when all params cleared
ok 7 - returns root when all params cleared
---
duration_ms: 0.0871
...
1..7
ok 6 - URL Building - State to URL
---
duration_ms: 1.0559
type: 'suite'
...
# Subtest: Complex URL Scenarios
# Subtest: handles all params together
ok 1 - handles all params together
---
duration_ms: 0.1183
...
# Subtest: empty string values treated as null/empty
ok 2 - empty string values treated as null/empty
---
duration_ms: 0.0817
...
1..2
ok 7 - Complex URL Scenarios
---
duration_ms: 0.2733
type: 'suite'
...
# Subtest: Deep Link Patterns - From Card Icons
# Subtest: SocialCard Graph icon: /?view=graph&task={id}
ok 1 - SocialCard Graph icon: /?view=graph&task={id}
---
duration_ms: 0.1285
...
# Subtest: SwarmCard Graph icon: /?view=graph&swarm={id}
ok 2 - SwarmCard Graph icon: /?view=graph&swarm={id}
---
duration_ms: 0.1297
...
# Subtest: SwarmCard Timeline icon: /?view=activity&swarm={id}
ok 3 - SwarmCard Timeline icon: /?view=activity&swarm={id}
---
duration_ms: 0.0782
...
# Subtest: Agent avatar click: /?view=activity&agent={id}
ok 4 - Agent avatar click: /?view=activity&agent={id}
---
duration_ms: 0.0769
...
1..4
ok 8 - Deep Link Patterns - From Card Icons
---
duration_ms: 0.5198
type: 'suite'
...
1..8
ok 1 - URL State Integration - bb-ui2.22
---
duration_ms: 7.8584
type: 'suite'
...
1..1
# tests 31
# suites 9
# pass 31
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 435.1391
TAP version 13
# Subtest: useGraphAnalysis - module exports
ok 1 - useGraphAnalysis - module exports
---
duration_ms: 29.8133
...
# Subtest: useGraphAnalysis underlying logic - graphModel is built correctly
ok 2 - useGraphAnalysis underlying logic - graphModel is built correctly
---
duration_ms: 10.1561
...
# Subtest: useGraphAnalysis underlying logic - cycleNodeIdSet detects cycles
ok 3 - useGraphAnalysis underlying logic - cycleNodeIdSet detects cycles
---
duration_ms: 0.5465
...
# Subtest: useGraphAnalysis underlying logic - cycleNodeIdSet empty for acyclic graph
ok 4 - useGraphAnalysis underlying logic - cycleNodeIdSet empty for acyclic graph
---
duration_ms: 0.1687
...
# Subtest: useGraphAnalysis underlying logic - blockerAnalysis returns blockers
ok 5 - useGraphAnalysis underlying logic - blockerAnalysis returns blockers
---
duration_ms: 0.4643
...
# Subtest: useGraphAnalysis underlying logic - blockerTooltipMap shows blocker info
ok 6 - useGraphAnalysis underlying logic - blockerTooltipMap shows blocker info
---
duration_ms: 0.3071
...
1..6
# tests 6
# suites 0
# pass 6
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 559.0462
TAP version 13
# Subtest: SmartDag - file exists and exports
ok 1 - SmartDag - file exists and exports
---
duration_ms: 4.3779
...
# Subtest: SmartDag - contains Filters toggle button
ok 2 - SmartDag - contains Filters toggle button
---
duration_ms: 0.949
...
# Subtest: SmartDag - contains Assign toggle button
ok 3 - SmartDag - contains Assign toggle button
---
duration_ms: 0.7066
...
# Subtest: SmartDag - contains WorkflowTabs
ok 4 - SmartDag - contains WorkflowTabs
---
duration_ms: 1.1698
...
# Subtest: SmartDag - supports onAssignModeChange callback
ok 5 - SmartDag - supports onAssignModeChange callback
---
duration_ms: 1.4824
...
# Subtest: SmartDag - supports onSelectedIssueChange callback
ok 6 - SmartDag - supports onSelectedIssueChange callback
---
duration_ms: 0.8891
...
# Subtest: SmartDag - imports TaskCardGrid
ok 7 - SmartDag - imports TaskCardGrid
---
duration_ms: 0.93
...
# Subtest: SmartDag - imports WorkflowGraph
ok 8 - SmartDag - imports WorkflowGraph
---
duration_ms: 0.8032
...
# Subtest: SmartDag - passes assignMode to WorkflowGraph
ok 9 - SmartDag - passes assignMode to WorkflowGraph
---
duration_ms: 0.9817
...
# Subtest: SmartDag - manages hideClosed filter
ok 10 - SmartDag - manages hideClosed filter
---
duration_ms: 1.1005
...
# Subtest: SmartDag - manages sortReadyFirst filter
ok 11 - SmartDag - manages sortReadyFirst filter
---
duration_ms: 0.7155
...
# Subtest: SmartDag - uses useGraphAnalysis hook
ok 12 - SmartDag - uses useGraphAnalysis hook
---
duration_ms: 0.6575
...
1..12
# tests 12
# suites 0
# pass 12
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 326.9817
TAP version 13
# Subtest: UnifiedShell - file exists and exports
ok 1 - UnifiedShell - file exists and exports
---
duration_ms: 4.1613
...
# Subtest: UnifiedShell - has assignMode state
ok 2 - UnifiedShell - has assignMode state
---
duration_ms: 1.0953
...
# Subtest: UnifiedShell - has selectedAssignIssue state
ok 3 - UnifiedShell - has selectedAssignIssue state
---
duration_ms: 0.7628
...
# Subtest: UnifiedShell - passes onAssignModeChange to SmartDag
ok 4 - UnifiedShell - passes onAssignModeChange to SmartDag
---
duration_ms: 0.7559
...
# Subtest: UnifiedShell - passes onSelectedIssueChange to SmartDag
ok 5 - UnifiedShell - passes onSelectedIssueChange to SmartDag
---
duration_ms: 1.3606
...
# Subtest: UnifiedShell - imports AssignmentPanel
ok 6 - UnifiedShell - imports AssignmentPanel
---
duration_ms: 0.9392
...
# Subtest: UnifiedShell - checks bd health and renders setup warning
ok 7 - UnifiedShell - checks bd health and renders setup warning
---
duration_ms: 0.7768
...
# Subtest: UnifiedShell - renders AssignmentPanel conditionally
not ok 8 - UnifiedShell - renders AssignmentPanel conditionally
---
duration_ms: 1.9496
location: 'C:\\Users\\Zenchant\\codex\\beadboard\\tests\\components\\unified-shell.test.tsx:2:3626'
failureType: 'testCodeFailure'
error: 'Should check view === graph && assignMode condition for AssignmentPanel'
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected: true
actual: false
operator: '=='
stack: |-
TestContext.<anonymous> (C:\Users\Zenchant\codex\beadboard\tests\components\unified-shell.test.tsx:53:10)
async Test.run (node:internal/test_runner/test:935:9)
async Test.processPendingSubtests (node:internal/test_runner/test:633:7)
...
# Subtest: UnifiedShell - does not import SwarmWorkspace
ok 9 - UnifiedShell - does not import SwarmWorkspace
---
duration_ms: 1.647
...
# Subtest: UnifiedShell - does not import SwarmMissionPicker
ok 10 - UnifiedShell - does not import SwarmMissionPicker
---
duration_ms: 1.0699
...
1..10
# tests 10
# suites 0
# pass 9
# fail 1
# cancelled 0
# skipped 0
# todo 0
# duration_ms 309.2239

1395
.beads/npm_test_output3.txt Normal file

File diff suppressed because it is too large Load diff

3
.beads/task1.txt Normal file
View file

@ -0,0 +1,3 @@
✓ Created issue: beadboard-txj.1 — Implement graph transitive reduction algorithm
Priority: P0
Status: open

3
.beads/task2.txt Normal file
View file

@ -0,0 +1,3 @@
✓ Created issue: beadboard-txj.2 — Apply Status Colors and Transitive Context to Graph Edges
Priority: P0
Status: open

3
.beads/task3.txt Normal file
View file

@ -0,0 +1,3 @@
✓ Created issue: beadboard-txj.3 — Implement Graph Focus (Selection) Interactivity
Priority: P1
Status: open

790
.beads/test_output.txt Normal file
View file

@ -0,0 +1,790 @@
TAP version 13
# Subtest: bootstrap scaffold files exist
ok 1 - bootstrap scaffold files exist
---
duration_ms: 1.7879
...
# Subtest: package.json has next/react/typescript scripts and deps
ok 2 - package.json has next/react/typescript scripts and deps
---
duration_ms: 0.7269
...
1..2
# tests 2
# suites 0
# pass 2
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 109.641
TAP version 13
# Subtest: BaseCard Component Contract
# Subtest: exports BaseCard component
ok 1 - exports BaseCard component
---
duration_ms: 49.7712
...
1..1
ok 1 - BaseCard Component Contract
---
duration_ms: 50.4334
type: 'suite'
...
# Subtest: BaseCard Styling Logic
# Subtest: should be possible to import the component
ok 1 - should be possible to import the component
---
duration_ms: 1.6802
...
# Subtest: applies correct status border class for "ready" status
ok 2 - applies correct status border class for "ready" status
---
duration_ms: 14.5164
...
# Subtest: applies correct status border class for "blocked" status
ok 3 - applies correct status border class for "blocked" status
---
duration_ms: 5.9515
...
# Subtest: applies selection ring when selected prop is true
ok 4 - applies selection ring when selected prop is true
---
duration_ms: 2.9283
...
1..4
ok 2 - BaseCard Styling Logic
---
duration_ms: 25.6463
type: 'suite'
...
1..2
# tests 5
# suites 2
# pass 5
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 387.1692
TAP version 13
# Subtest: AgentAvatar Component Contract
# Subtest: exports AgentAvatar component
ok 1 - exports AgentAvatar component
---
duration_ms: 116.5064
...
1..1
ok 1 - AgentAvatar Component Contract
---
duration_ms: 117.1492
type: 'suite'
...
# Subtest: AgentAvatar Role Styling
# Subtest: applies correct role color class for "ui" role
ok 1 - applies correct role color class for "ui" role
---
duration_ms: 7.4888
...
# Subtest: applies correct role color class for "orchestrator" role
ok 2 - applies correct role color class for "orchestrator" role
---
duration_ms: 1.8762
...
1..2
ok 2 - AgentAvatar Role Styling
---
duration_ms: 9.6138
type: 'suite'
...
# Subtest: AgentAvatar ZFC States
# Subtest: applies working pulse glow
ok 1 - applies working pulse glow
---
duration_ms: 1.3946
...
1..1
ok 3 - AgentAvatar ZFC States
---
duration_ms: 1.6685
type: 'suite'
...
1..3
# tests 4
# suites 3
# pass 4
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 435.7079
TAP version 13
# Subtest: SessionsHeader: Agent Grouping
# Subtest: groups agents by swarm
ok 1 - groups agents by swarm
---
duration_ms: 2.0443
...
# Subtest: shows fallback bucket for unassigned agents
ok 2 - shows fallback bucket for unassigned agents
---
duration_ms: 0.1574
...
# Subtest: handles empty swarm groups
ok 3 - handles empty swarm groups
---
duration_ms: 0.1261
...
1..3
ok 1 - SessionsHeader: Agent Grouping
---
duration_ms: 3.2814
type: 'suite'
...
1..1
# tests 3
# suites 1
# pass 3
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 267.9904
TAP version 13
# Subtest: getAgentRoleColor returns correct color for known roles
ok 1 - getAgentRoleColor returns correct color for known roles
---
duration_ms: 0.7796
...
# Subtest: getAgentRoleColor returns default for unknown role
ok 2 - getAgentRoleColor returns default for unknown role
---
duration_ms: 0.1221
...
1..2
# tests 2
# suites 0
# pass 2
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 250.2131
TAP version 13
# Subtest: parseIssuesJsonl applies defaults and preserves priority 0
ok 1 - parseIssuesJsonl applies defaults and preserves priority 0
---
duration_ms: 1.1101
...
# Subtest: parseIssuesJsonl skips malformed and blank lines
ok 2 - parseIssuesJsonl skips malformed and blank lines
---
duration_ms: 0.1893
...
# Subtest: parseIssuesJsonl filters tombstones by default
ok 3 - parseIssuesJsonl filters tombstones by default
---
duration_ms: 0.148
...
# Subtest: parseIssuesJsonl can include tombstones when requested
ok 4 - parseIssuesJsonl can include tombstones when requested
---
duration_ms: 0.1451
...
# Subtest: parseIssuesJsonl supports beads dependency schema with depends_on_id and parent-child
ok 5 - parseIssuesJsonl supports beads dependency schema with depends_on_id and parent-child
---
duration_ms: 0.7609
...
1..5
# tests 5
# suites 0
# pass 5
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 275.8118
TAP version 13
# Subtest: canonicalizeWindowsPath normalizes separators and drive casing
ok 1 - canonicalizeWindowsPath normalizes separators and drive casing
---
duration_ms: 0.9571
...
# Subtest: windowsPathKey is case-insensitive stable key
ok 2 - windowsPathKey is case-insensitive stable key
---
duration_ms: 0.2276
...
# Subtest: toDisplayPath renders forward slashes for UI readability
ok 3 - toDisplayPath renders forward slashes for UI readability
---
duration_ms: 0.1456
...
# Subtest: sameWindowsPath handles case/separator differences
ok 4 - sameWindowsPath handles case/separator differences
---
duration_ms: 0.1285
...
1..4
# tests 4
# suites 0
# pass 4
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 257.9372
TAP version 13
# Subtest: LeftPanel Component Contract
# Subtest: exports LeftPanel component
ok 1 - exports LeftPanel component
---
duration_ms: 476.382
...
# Subtest: LeftPanel accepts issues and onEpicSelect props
ok 2 - LeftPanel accepts issues and onEpicSelect props
---
duration_ms: 2.427
...
1..2
ok 1 - LeftPanel Component Contract
---
duration_ms: 480.2344
type: 'suite'
...
# Subtest: LeftPanel Tree Structure
# Subtest: renders epics as expandable tree items
ok 1 - renders epics as expandable tree items
---
duration_ms: 2.4781
...
# Subtest: groups beads under their parent epic
ok 2 - groups beads under their parent epic
---
duration_ms: 2.3032
...
1..2
ok 2 - LeftPanel Tree Structure
---
duration_ms: 5.297
type: 'suite'
...
# Subtest: LeftPanel Responsive Behavior
# Subtest: applies responsive classes for desktop, tablet, and mobile
ok 1 - applies responsive classes for desktop, tablet, and mobile
---
duration_ms: 2.5744
...
1..1
ok 3 - LeftPanel Responsive Behavior
---
duration_ms: 2.8537
type: 'suite'
...
# Subtest: LeftPanel Scope Controls
# Subtest: renders scope section
ok 1 - renders scope section
---
duration_ms: 2.4409
...
1..1
ok 4 - LeftPanel Scope Controls
---
duration_ms: 3.207
type: 'suite'
...
1..4
# tests 6
# suites 4
# pass 6
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 762.0118
TAP version 13
# Subtest: TopBar Component Contract
# Subtest: exports TopBar component
ok 1 - exports TopBar component
---
duration_ms: 928.6733
...
# Subtest: TopBar component can be imported without errors
ok 2 - TopBar component can be imported without errors
---
duration_ms: 1.5359
...
1..2
ok 1 - TopBar Component Contract
---
duration_ms: 931.4628
type: 'suite'
...
# Subtest: TopBar View Tabs
# Subtest: renders view tabs: Social, Graph
ok 1 - renders view tabs: Social, Graph
---
duration_ms: 2.2099
...
# Subtest: active tab has bold text and accent underline
ok 2 - active tab has bold text and accent underline
---
duration_ms: 1.9226
...
1..2
ok 2 - TopBar View Tabs
---
duration_ms: 4.5329
type: 'suite'
...
# Subtest: TopBar Filter and Controls
# Subtest: renders filter/search input placeholder
ok 1 - renders filter/search input placeholder
---
duration_ms: 1.6872
...
# Subtest: renders settings placeholder
ok 2 - renders settings placeholder
---
duration_ms: 1.1945
...
1..2
ok 3 - TopBar Filter and Controls
---
duration_ms: 3.3144
type: 'suite'
...
1..3
# tests 6
# suites 3
# pass 6
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 1275.1308
TAP version 13
# Subtest: Mobile Navigation - Hamburger Menu
# Subtest: exports MobileNav component
ok 1 - exports MobileNav component
---
duration_ms: 146.0955
...
# Subtest: renders tab buttons: Social, Graph
ok 2 - renders tab buttons: Social, Graph
---
duration_ms: 2.4626
...
# Subtest: highlights active tab with accent color
ok 3 - highlights active tab with accent color
---
duration_ms: 2.4243
...
# Subtest: uses setView from useUrlState on tab click
ok 4 - uses setView from useUrlState on tab click
---
duration_ms: 2.3011
...
1..4
ok 1 - Mobile Navigation - Hamburger Menu
---
duration_ms: 155.4009
type: 'suite'
...
# Subtest: TopBar Hamburger Menu
# Subtest: shows hamburger button on mobile and tablet
ok 1 - shows hamburger button on mobile and tablet
---
duration_ms: 594.7571
...
# Subtest: hamburger button opens left panel drawer
ok 2 - hamburger button opens left panel drawer
---
duration_ms: 1.5433
...
# Subtest: hides hamburger on desktop
ok 3 - hides hamburger on desktop
---
duration_ms: 1.3406
...
1..3
ok 2 - TopBar Hamburger Menu
---
duration_ms: 598.2042
type: 'suite'
...
1..2
# tests 7
# suites 2
# pass 7
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 1074.08
TAP version 13
# Subtest: SwarmCard Component Contract
# Subtest: exports SwarmCard component
ok 1 - exports SwarmCard component
---
duration_ms: 301.3252
...
# Subtest: SwarmCard component can be imported without errors
ok 2 - SwarmCard component can be imported without errors
---
duration_ms: 1.6098
...
1..2
ok 1 - SwarmCard Component Contract
---
duration_ms: 304.0952
type: 'suite'
...
# Subtest: SwarmCard Agent Roster
# Subtest: renders agent avatars with liveness glow
ok 1 - renders agent avatars with liveness glow
---
duration_ms: 2.012
...
# Subtest: displays agent current task when available
ok 2 - displays agent current task when available
---
duration_ms: 1.5509
...
1..2
ok 2 - SwarmCard Agent Roster
---
duration_ms: 3.8907
type: 'suite'
...
# Subtest: SwarmCard Progress Bar
# Subtest: renders progress bar showing completion percentage
ok 1 - renders progress bar showing completion percentage
---
duration_ms: 1.4962
...
1..1
ok 3 - SwarmCard Progress Bar
---
duration_ms: 1.7516
type: 'suite'
...
# Subtest: SwarmCard Attention Items
# Subtest: renders attention items with warning styling
ok 1 - renders attention items with warning styling
---
duration_ms: 2.1433
...
1..1
ok 4 - SwarmCard Attention Items
---
duration_ms: 2.818
type: 'suite'
...
# Subtest: SwarmCard View-Jump Icons
# Subtest: renders view-jump icons for navigation
ok 1 - renders view-jump icons for navigation
---
duration_ms: 2.1341
...
1..1
ok 5 - SwarmCard View-Jump Icons
---
duration_ms: 2.4614
type: 'suite'
...
1..5
# tests 7
# suites 5
# pass 7
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 612.9682
TAP version 13
# Subtest: URL State Integration - bb-ui2.22
# Subtest: Valid URL Patterns - Social View
# Subtest: /?view=social - defaults to social view
ok 1 - /?view=social - defaults to social view
---
duration_ms: 0.9839
...
# Subtest: /?view=social&task=bb-buff.1&panel=open - task selected, panel open
ok 2 - /?view=social&task=bb-buff.1&panel=open - task selected, panel open
---
duration_ms: 0.2403
...
# Subtest: /?view=social&task=bb-ui2.22 - task with dots in ID
ok 3 - /?view=social&task=bb-ui2.22 - task with dots in ID
---
duration_ms: 0.1425
...
1..3
ok 1 - Valid URL Patterns - Social View
---
duration_ms: 2.0615
type: 'suite'
...
# Subtest: Valid URL Patterns - Graph View
# Subtest: /?view=graph - graph view default
not ok 1 - /?view=graph - graph view default
---
duration_ms: 3.8342
location: 'C:\\Users\\Zenchant\\codex\\beadboard\\tests\\hooks\\url-state-integration.test.ts:2:2578'
failureType: 'testCodeFailure'
error: |-
Expected values to be strictly equal:
'overview' !== 'flow'
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected: 'flow'
actual: 'overview'
operator: 'strictEqual'
stack: |-
TestContext.<anonymous> (C:\Users\Zenchant\codex\beadboard\tests\hooks\url-state-integration.test.ts:53:14)
Test.runInAsyncScope (node:async_hooks:211:14)
Test.run (node:internal/test_runner/test:934:25)
Test.start (node:internal/test_runner/test:833:17)
node:internal/test_runner/test:1318:71
node:internal/per_context/primordials:483:82
new Promise (<anonymous>)
new SafePromise (node:internal/per_context/primordials:451:29)
node:internal/per_context/primordials:483:9
Array.map (<anonymous>)
...
# Subtest: /?view=graph&task=bb-buff.1 - graph with task selected
ok 2 - /?view=graph&task=bb-buff.1 - graph with task selected
---
duration_ms: 0.2386
...
# Subtest: /?view=graph&graphTab=flow - flow tab selected
ok 3 - /?view=graph&graphTab=flow - flow tab selected
---
duration_ms: 0.1616
...
# Subtest: /?view=graph&graphTab=overview - overview tab selected
ok 4 - /?view=graph&graphTab=overview - overview tab selected
---
duration_ms: 0.1198
...
# Subtest: /?view=graph&swarm=bb-buff - graph filtered by swarm
ok 5 - /?view=graph&swarm=bb-buff - graph filtered by swarm
---
duration_ms: 0.2708
...
1..5
not ok 2 - Valid URL Patterns - Graph View
---
duration_ms: 5.0492
type: 'suite'
location: 'C:\\Users\\Zenchant\\codex\\beadboard\\tests\\hooks\\url-state-integration.test.ts:2:2515'
failureType: 'subtestsFailed'
error: '1 subtest failed'
code: 'ERR_TEST_FAILURE'
...
# Subtest: Deprecated Swarm View Fallback
# Subtest: /?view=swarm - falls back to social (swarm view deprecated)
ok 1 - /?view=swarm - falls back to social (swarm view deprecated)
---
duration_ms: 0.2049
...
# Subtest: /?view=swarm&swarm=bb-buff - falls back to social but preserves swarmId
ok 2 - /?view=swarm&swarm=bb-buff - falls back to social but preserves swarmId
---
duration_ms: 0.271
...
# Subtest: /?view=swarm&swarm=bb-buff&panel=open - falls back to social with panel open
ok 3 - /?view=swarm&swarm=bb-buff&panel=open - falls back to social with panel open
---
duration_ms: 0.1949
...
1..3
ok 3 - Deprecated Swarm View Fallback
---
duration_ms: 0.8363
type: 'suite'
...
# Subtest: Valid URL Patterns - Activity View
# Subtest: /?view=activity - activity view default
ok 1 - /?view=activity - activity view default
---
duration_ms: 0.1634
...
# Subtest: /?view=activity&agent=bb-silver-castle - filtered by agent
ok 2 - /?view=activity&agent=bb-silver-castle - filtered by agent
---
duration_ms: 0.0883
...
# Subtest: /?view=activity&swarm=bb-buff - filtered by swarm
ok 3 - /?view=activity&swarm=bb-buff - filtered by swarm
---
duration_ms: 0.0828
...
1..3
ok 4 - Valid URL Patterns - Activity View
---
duration_ms: 0.5275
type: 'suite'
...
# Subtest: Invalid Param Handling
# Subtest: /?view=invalid - invalid view defaults to social
ok 1 - /?view=invalid - invalid view defaults to social
---
duration_ms: 0.167
...
# Subtest: /?view=graph&graphTab=invalid - invalid graphTab defaults to flow
not ok 2 - /?view=graph&graphTab=invalid - invalid graphTab defaults to flow
---
duration_ms: 0.3965
location: 'C:\\Users\\Zenchant\\codex\\beadboard\\tests\\hooks\\url-state-integration.test.ts:2:6479'
failureType: 'testCodeFailure'
error: |-
Expected values to be strictly equal:
'overview' !== 'flow'
code: 'ERR_ASSERTION'
name: 'AssertionError'
expected: 'flow'
actual: 'overview'
operator: 'strictEqual'
stack: |-
TestContext.<anonymous> (C:\Users\Zenchant\codex\beadboard\tests\hooks\url-state-integration.test.ts:138:14)
Test.runInAsyncScope (node:async_hooks:211:14)
Test.run (node:internal/test_runner/test:934:25)
Suite.processPendingSubtests (node:internal/test_runner/test:633:18)
Test.postRun (node:internal/test_runner/test:1045:19)
Test.run (node:internal/test_runner/test:973:12)
async Promise.all (index 0)
async Suite.run (node:internal/test_runner/test:1320:7)
async Suite.processPendingSubtests (node:internal/test_runner/test:633:7)
...
# Subtest: /?panel=invalid - invalid panel defaults to open
ok 3 - /?panel=invalid - invalid panel defaults to open
---
duration_ms: 0.1068
...
# Subtest: /?task=invalid-id - invalid task ID still parsed (no validation)
ok 4 - /?task=invalid-id - invalid task ID still parsed (no validation)
---
duration_ms: 0.0904
...
1..4
not ok 5 - Invalid Param Handling
---
duration_ms: 0.9102
type: 'suite'
location: 'C:\\Users\\Zenchant\\codex\\beadboard\\tests\\hooks\\url-state-integration.test.ts:2:6178'
failureType: 'subtestsFailed'
error: '1 subtest failed'
code: 'ERR_TEST_FAILURE'
...
# Subtest: URL Building - State to URL
# Subtest: builds social view URL
ok 1 - builds social view URL
---
duration_ms: 0.2367
...
# Subtest: builds graph view with task URL
ok 2 - builds graph view with task URL
---
duration_ms: 0.0983
...
# Subtest: builds swarm view with swarm param
ok 3 - builds swarm view with swarm param
---
duration_ms: 0.0888
...
# Subtest: builds activity view with agent filter
ok 4 - builds activity view with agent filter
---
duration_ms: 0.159
...
# Subtest: preserves existing params when adding new ones
ok 5 - preserves existing params when adding new ones
---
duration_ms: 0.1957
...
# Subtest: removes params when set to null
ok 6 - removes params when set to null
---
duration_ms: 0.1242
...
# Subtest: returns root when all params cleared
ok 7 - returns root when all params cleared
---
duration_ms: 0.0988
...
1..7
ok 6 - URL Building - State to URL
---
duration_ms: 1.1817
type: 'suite'
...
# Subtest: Complex URL Scenarios
# Subtest: handles all params together
ok 1 - handles all params together
---
duration_ms: 0.1361
...
# Subtest: empty string values treated as null/empty
ok 2 - empty string values treated as null/empty
---
duration_ms: 0.0964
...
1..2
ok 7 - Complex URL Scenarios
---
duration_ms: 0.317
type: 'suite'
...
# Subtest: Deep Link Patterns - From Card Icons
# Subtest: SocialCard Graph icon: /?view=graph&task={id}
ok 1 - SocialCard Graph icon: /?view=graph&task={id}
---
duration_ms: 0.1474
...
# Subtest: SwarmCard Graph icon: /?view=graph&swarm={id}
ok 2 - SwarmCard Graph icon: /?view=graph&swarm={id}
---
duration_ms: 0.086
...
# Subtest: SwarmCard Timeline icon: /?view=activity&swarm={id}
ok 3 - SwarmCard Timeline icon: /?view=activity&swarm={id}
---
duration_ms: 0.2482
...
# Subtest: Agent avatar click: /?view=activity&agent={id}
ok 4 - Agent avatar click: /?view=activity&agent={id}
---
duration_ms: 0.1616
...
1..4
ok 8 - Deep Link Patterns - From Card Icons
---
duration_ms: 0.8134
type: 'suite'
...
1..8
not ok 1 - URL State Integration - bb-ui2.22
---
duration_ms: 12.6296
type: 'suite'
location: 'C:\\Users\\Zenchant\\codex\\beadboard\\tests\\hooks\\url-state-integration.test.ts:2:1269'
failureType: 'subtestsFailed'
error: '2 subtests failed'
code: 'ERR_TEST_FAILURE'
...
1..1
# tests 31
# suites 9
# pass 29
# fail 2
# cancelled 0
# skipped 0
# todo 0
# duration_ms 481.6532

1
.claude/skills/shadcn-ui Symbolic link
View file

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

File diff suppressed because it is too large Load diff

View file

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

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

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

1
.cline/skills/shadcn-ui Symbolic link
View file

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

File diff suppressed because it is too large Load diff

View file

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

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

@ -1,586 +0,0 @@
# 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 +1 @@
{}
{"user.email":"jordanlive121@gmail.com","user.name":"zenchant"}

1
.openhands/skills/shadcn-ui Symbolic link
View file

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

File diff suppressed because it is too large Load diff

View file

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

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

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

@ -0,0 +1,533 @@
# BeadBoard Global Install Runtime Manager Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** Replace repo-path shim install behavior with a robust global install model where `beadboard` runs from managed runtime home and works from any directory.
**Architecture:** Keep a hybrid install strategy with npm-global as primary and script bootstrap as fallback, but unify both paths behind one runtime manager contract (`~/.beadboard/runtime/<version>` + stable shims). Preserve backward compatibility by detecting legacy repo-bound shims and offering migration. Implement with strict TDD and explicit installer smoke tests.
**Tech Stack:** Node.js (ESM), TypeScript/tsx for CLI code, PowerShell + POSIX shell wrappers, GitHub Actions, Node test runner.
---
## Pre-Implementation Checklist
### Task 0: Baseline and Safety Snapshot
**Files:**
- Modify: `beadboard/.beads/issues.jsonl` via `bd` commands only
- Read: `/mnt/c/Users/Zenchant/codex/beadboard/AGENTS.md`
- Read: `/mnt/c/Users/Zenchant/codex/beadboard/NEXT_SESSION_PROMPT.md`
**Step 1: Claim/track implementation bead(s)**
Run:
```bash
cd /mnt/c/Users/Zenchant/codex/beadboard
bd create --title="Global installer runtime manager implementation" --description="Implement npm-global-first runtime manager with migration from repo-path shims" --type=task --priority=1 --label="installation,cli,runtime"
bd update <new-bead-id> --status in_progress --assignee <agent-bead-id>
```
**Step 2: Capture baseline gate status**
Run:
```bash
npm run typecheck
npm run lint
npm run test
```
Expected: existing known unrelated failures may appear; record exact failing files/tests in bead notes.
**Step 3: Commit checkpoint**
No code changes yet. Do not commit.
---
## Phase 1: Define Runtime Manager Contract
### Task 1: Add runtime manager ADR and package contract
**Files:**
- Create: `docs/adr/2026-03-03-runtime-manager-global-install.md`
- Modify: `docs/adr/2026-03-03-global-installer-contract-and-manifest.md`
**Step 1: Write failing docs contract test**
Create:
- `tests/docs/runtime-manager-adr-contract.test.ts`
```ts
import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
import path from 'node:path';
test('runtime manager ADR exists and declares runtime home strategy', async () => {
const raw = await fs.readFile(path.resolve('docs/adr/2026-03-03-runtime-manager-global-install.md'), 'utf8');
assert.match(raw, /~\/\.beadboard\/runtime/i);
assert.match(raw, /npm i -g beadboard/i);
assert.match(raw, /legacy repo-bound shim migration/i);
});
```
**Step 2: Run test to verify it fails**
Run:
```bash
node --import tsx --test tests/docs/runtime-manager-adr-contract.test.ts
```
Expected: FAIL (`ENOENT` for missing ADR file).
**Step 3: Write minimal implementation**
Add ADR with:
- runtime directory layout
- shim strategy
- update/uninstall model
- compatibility migration model
- failure modes and rollback
**Step 4: Run test to verify it passes**
Run:
```bash
node --import tsx --test tests/docs/runtime-manager-adr-contract.test.ts
```
Expected: PASS.
**Step 5: Commit**
```bash
git add tests/docs/runtime-manager-adr-contract.test.ts docs/adr/2026-03-03-runtime-manager-global-install.md docs/adr/2026-03-03-global-installer-contract-and-manifest.md
git commit -m "docs: define runtime manager global install contract"
```
---
## Phase 2: Implement Runtime Manager Library
### Task 2: Add runtime manager core module with strict validation
**Files:**
- Create: `src/lib/runtime-manager.ts`
- Create: `tests/lib/runtime-manager.test.ts`
**Step 1: Write failing test**
```ts
import test from 'node:test';
import assert from 'node:assert/strict';
import { getRuntimePaths, normalizeVersion } from '../../src/lib/runtime-manager';
test('normalizeVersion supports semver and rejects empty', () => {
assert.equal(normalizeVersion('1.2.3'), '1.2.3');
assert.throws(() => normalizeVersion(''));
});
test('getRuntimePaths builds ~/.beadboard/runtime/<version> layout', () => {
const p = getRuntimePaths('/tmp/home', '1.2.3');
assert.match(p.runtimeRoot, /runtime\/1\.2\.3$/);
assert.match(p.shimDir, /\.beadboard\/bin$/);
});
```
**Step 2: Run test to verify it fails**
Run:
```bash
node --import tsx --test tests/lib/runtime-manager.test.ts
```
Expected: FAIL (`Cannot find module '../../src/lib/runtime-manager'`).
**Step 3: Write minimal implementation**
Implement exports:
- `normalizeVersion(version: string): string`
- `getRuntimePaths(home: string, version: string)`
- `resolveInstallHome(env)`
Keep pure and side-effect free.
**Step 4: Run test to verify it passes**
Run:
```bash
node --import tsx --test tests/lib/runtime-manager.test.ts
```
Expected: PASS.
**Step 5: Commit**
```bash
git add src/lib/runtime-manager.ts tests/lib/runtime-manager.test.ts
git commit -m "feat(installer): add runtime manager core library"
```
---
## Phase 3: Move Launcher to Runtime-Aware Execution
### Task 3: Update launcher to run from managed runtime root
**Files:**
- Modify: `install/beadboard.mjs`
- Modify: `tests/scripts/beadboard-launcher.test.ts`
- Create: `tests/scripts/beadboard-launcher-runtime.test.ts`
**Step 1: Write failing test**
```ts
import test from 'node:test';
import assert from 'node:assert/strict';
import { execFile } from 'node:child_process';
import { promisify } from 'node:util';
import path from 'node:path';
const execFileAsync = promisify(execFile);
const launcherPath = path.resolve('install/beadboard.mjs');
test('status --json reports runtime root and install mode', async () => {
const { stdout } = await execFileAsync(process.execPath, [launcherPath, 'status', '--json']);
const payload = JSON.parse(stdout);
assert.ok(payload.runtimeRoot);
assert.ok(payload.installMode);
});
```
**Step 2: Run test to verify it fails**
Run:
```bash
node --import tsx --test tests/scripts/beadboard-launcher-runtime.test.ts
```
Expected: FAIL (missing fields).
**Step 3: Write minimal implementation**
In launcher:
- derive runtime home/version
- include `runtimeRoot`, `installMode`, `shimTarget` in JSON status
- preserve existing `start/open/status` behavior
**Step 4: Run tests to verify pass**
Run:
```bash
node --import tsx --test tests/scripts/beadboard-launcher.test.ts
node --import tsx --test tests/scripts/beadboard-launcher-runtime.test.ts
```
Expected: PASS.
**Step 5: Commit**
```bash
git add install/beadboard.mjs tests/scripts/beadboard-launcher.test.ts tests/scripts/beadboard-launcher-runtime.test.ts
git commit -m "feat(launcher): add runtime-aware status metadata"
```
---
## Phase 4: Replace Repo-Bound Shim Targets
### Task 4: Make install wrappers point at runtime-managed target
**Files:**
- Modify: `install/install.sh`
- Modify: `install/install.ps1`
- Modify: `tests/scripts/install-wrappers-contract.test.ts`
- Modify: `tests/scripts/install-sh-smoke.test.ts`
- Create: `tests/scripts/install-legacy-migration.test.ts`
**Step 1: Write failing migration test**
```ts
import test from 'node:test';
import assert from 'node:assert/strict';
// simulate legacy shim text and verify installer rewrites to runtime target
test('installer migrates legacy repo-bound shim to runtime-managed shim', async () => {
assert.fail('implement');
});
```
**Step 2: Run test to verify it fails**
Run:
```bash
node --import tsx --test tests/scripts/install-legacy-migration.test.ts
```
Expected: FAIL.
**Step 3: Write minimal implementation**
In both wrappers:
- install runtime metadata file (`~/.beadboard/runtime/current.json`)
- rewrite `bb`/`beadboard` shims to resolve runtime target first
- detect old shim signatures and replace atomically
**Step 4: Run wrapper tests**
Run:
```bash
node --import tsx --test tests/scripts/install-wrappers-contract.test.ts
node --import tsx --test tests/scripts/install-sh-smoke.test.ts
node --import tsx --test tests/scripts/install-legacy-migration.test.ts
```
Expected: PASS.
**Step 5: Commit**
```bash
git add install/install.sh install/install.ps1 tests/scripts/install-wrappers-contract.test.ts tests/scripts/install-sh-smoke.test.ts tests/scripts/install-legacy-migration.test.ts
git commit -m "feat(installer): migrate shims to runtime-managed targets"
```
---
## Phase 5: Add npm-Global Entry and Self-Management Commands
### Task 5: Add CLI entrypoint commands (`self-update`, `uninstall`, `doctor`)
**Files:**
- Modify: `package.json`
- Create: `bin/beadboard.js`
- Create: `src/cli/beadboard-cli.ts`
- Create: `tests/cli/beadboard-cli.test.ts`
**Step 1: Write failing CLI tests**
```ts
import test from 'node:test';
import assert from 'node:assert/strict';
import { runCli } from '../../src/cli/beadboard-cli';
test('doctor returns structured install diagnostics', async () => {
const out = await runCli(['doctor', '--json']);
assert.equal(out.ok, true);
assert.ok(out.installMode);
});
```
**Step 2: Run test to verify it fails**
Run:
```bash
node --import tsx --test tests/cli/beadboard-cli.test.ts
```
Expected: FAIL (missing module/command).
**Step 3: Write minimal implementation**
Add commands:
- `beadboard doctor --json`
- `beadboard self-update` (placeholder behavior with explicit output if publish not configured)
- `beadboard uninstall` (remove shims/runtime with `--yes` guard)
Add `bin` mapping in `package.json`.
**Step 4: Run test to verify pass**
Run:
```bash
node --import tsx --test tests/cli/beadboard-cli.test.ts
```
Expected: PASS.
**Step 5: Commit**
```bash
git add package.json bin/beadboard.js src/cli/beadboard-cli.ts tests/cli/beadboard-cli.test.ts
git commit -m "feat(cli): add global entrypoint with doctor/update/uninstall commands"
```
---
## Phase 6: Driver Alignment + Remediation UX Finalization
### Task 6: Update driver preflight/remediation copy for npm-global-first flow
**Files:**
- Modify: `skills/beadboard-driver/scripts/lib/driver-lib.mjs`
- Modify: `skills/beadboard-driver/scripts/session-preflight.mjs`
- Modify: `tests/skills/beadboard-driver/resolve-bb.test.ts`
- Modify: `tests/skills/beadboard-driver/session-preflight.test.ts`
**Step 1: Write failing test assertion**
Add expectation:
- remediation mentions `npm i -g beadboard` as primary
- install script remains fallback
**Step 2: Run test to verify it fails**
Run:
```bash
node --import tsx --test tests/skills/beadboard-driver/resolve-bb.test.ts
node --import tsx --test tests/skills/beadboard-driver/session-preflight.test.ts
```
Expected: FAIL (copy mismatch).
**Step 3: Write minimal implementation**
Update remediation strings to:
1. npm-global install
2. fallback installer wrapper
3. BB_REPO override guidance
**Step 4: Re-run tests**
Run:
```bash
node --import tsx --test tests/skills/beadboard-driver/resolve-bb.test.ts
node --import tsx --test tests/skills/beadboard-driver/session-preflight.test.ts
```
Expected: PASS.
**Step 5: Commit**
```bash
git add skills/beadboard-driver/scripts/lib/driver-lib.mjs skills/beadboard-driver/scripts/session-preflight.mjs tests/skills/beadboard-driver/resolve-bb.test.ts tests/skills/beadboard-driver/session-preflight.test.ts
git commit -m "feat(driver): prefer npm-global remediation with installer fallback"
```
---
## Phase 7: CI, Docs, and Release Readiness
### Task 7: Expand CI smoke and operator docs for global model
**Files:**
- Modify: `.github/workflows/installer-smoke.yml`
- Modify: `README.md`
- Modify: `docs/adr/2026-03-03-runtime-manager-global-install.md`
- Create: `docs/ops/global-install-rollout.md`
- Modify: `tests/scripts/installer-ci-contract.test.ts`
- Modify: `tests/docs/installer-quickstart-contract.test.ts`
**Step 1: Write failing test updates**
Add assertions for:
- npm-global command in docs
- runtime home path mentions
- CI step validating `beadboard doctor --json`
**Step 2: Run tests to verify fail**
Run:
```bash
node --import tsx --test tests/scripts/installer-ci-contract.test.ts
node --import tsx --test tests/docs/installer-quickstart-contract.test.ts
```
Expected: FAIL.
**Step 3: Write minimal implementation**
Update docs and workflow:
- install paths
- migration notes
- recovery playbook
- supported platforms matrix
**Step 4: Re-run tests**
Run:
```bash
node --import tsx --test tests/scripts/installer-ci-contract.test.ts
node --import tsx --test tests/docs/installer-quickstart-contract.test.ts
```
Expected: PASS.
**Step 5: Commit**
```bash
git add .github/workflows/installer-smoke.yml README.md docs/adr/2026-03-03-runtime-manager-global-install.md docs/ops/global-install-rollout.md tests/scripts/installer-ci-contract.test.ts tests/docs/installer-quickstart-contract.test.ts
git commit -m "docs(ci): finalize global install runtime docs and smoke coverage"
```
---
## Phase 8: Final Verification + Bead Closeout
### Task 8: Full-gate verification and close
**Files:**
- Modify: `beadboard` issue notes via `bd update`
- Modify: `NEXT_SESSION_PROMPT.md`
**Step 1: Run full gates**
Run:
```bash
npm run typecheck
npm run lint
npm run test
```
Expected: PASS; if unrelated failures exist, capture exact files/tests.
**Step 2: Run targeted installer acceptance checks**
Run:
```bash
node --import tsx --test tests/lib/runtime-manager.test.ts
node --import tsx --test tests/scripts/beadboard-launcher-runtime.test.ts
node --import tsx --test tests/scripts/install-legacy-migration.test.ts
node --import tsx --test tests/skills/beadboard-driver/resolve-bb.test.ts
```
Expected: PASS.
**Step 3: Update beads with evidence**
Run:
```bash
bd update <bead-id> --notes "<commands run + pass/fail details>"
bd close <bead-id> --reason "<completed outcome>"
```
**Step 4: Update handoff**
Modify:
- `NEXT_SESSION_PROMPT.md` with shipped state + residual risks + next bead.
**Step 5: Commit**
```bash
git add NEXT_SESSION_PROMPT.md
git commit -m "chore: close runtime-manager rollout with verification evidence"
```
---
## References and Required Skills During Execution
1. `@test-driven-development`
2. `@verification-before-completion`
3. `@linus-beads-discipline`
4. `@beadboard-driver`
5. `@executing-plans` (required for implementation phase)
---
Plan complete and saved to `docs/plans/2026-03-03-global-install-runtime-manager.md`. Two execution options:
**1. Subagent-Driven (this session)** - I dispatch fresh subagent per task, review between tasks, fast iteration
**2. Parallel Session (separate)** - Open new session with executing-plans, batch execution with checkpoints
Which approach?

25
install/manifest.json Normal file
View file

@ -0,0 +1,25 @@
{
"version": "installer.v1",
"distribution": {
"packageName": "beadboard",
"shimNames": ["bb", "beadboard"]
},
"wrappers": {
"windows": {
"script": "install.ps1"
},
"posix": {
"script": "install.sh"
}
},
"runtime": {
"start": "beadboard start",
"open": "beadboard open",
"status": "beadboard status"
},
"driver": {
"remediationMode": "detect_only",
"installSideEffects": false
}
}

View file

@ -1,78 +1,158 @@
---
name: beadboard-driver
description: Drive BeadBoard agent workflows with strict Operative Protocol v1 compliance. Use when handling bead lifecycle work that combines bd status commands with bb agent coordination (register/adopt, activity-lease, reserve/release, send/ack), especially in multi-agent sessions requiring silent observability and collision avoidance.
description: Complete operating manual for agents running work in external repos while humans orchestrate from BeadBoard.
---
# Beadboard Driver (Operative Protocol v1)
# BeadBoard Driver
## Overview
BeadBoard is for teams that want autonomous agents without losing control of the work.
Use this skill to run repeatable `bd` + `bb` workflows under the **Activity Lease** (Parking Permit) model. Resolve `bb` safely, bootstrap via `bb-init`, coordinate via traceable incursions, and maintain liveness through real work.
Most agent setups break down the same way: work happens quickly, but visibility collapses, handoffs get fuzzy, and “done” starts meaning “probably done.” This skill fixes that operating problem.
## Core Workflow
With BeadBoard Driver, agents execute inside the target project repo, while humans orchestrate from BeadBoard as the control plane: assign, redirect, intervene, verify, and keep a durable coordination record.
BeadBoard project:
- GitHub: `https://github.com/zenchantlive/beadboard`
## What This Changes
- Work becomes observable, not performative.
- Ownership stays explicit at bead level.
- Handoffs and blockers become machine-readable events.
- Completion claims require evidence, not confidence.
- Multi-agent execution stays coordinated instead of chaotic.
## Operating Reality
- Agents usually run in a non-BeadBoard target repo.
- The user controls project scope from BeadBoard UI.
- Agents execute the current repo context they were assigned.
- `bd` remains source of truth for task/memory state.
## Start Here
Run this quick confidence check before you start a session:
1. **Bootstrap & Handshake**:
Run `bb-init` to resolve paths and identify yourself. Use `--adopt` if resuming a task with uncommitted changes.
```bash
node scripts/bb-init.mjs --register <agent-name> --role <role> --json
# OR
node scripts/bb-init.mjs --adopt <prior-agent-id> --non-interactive --json
bd --version
node skills/beadboard-driver/scripts/session-preflight.mjs
node skills/beadboard-driver/scripts/resolve-bb.mjs
```
2. **Claim Territory**:
Reserve your work surface before making edits to prevent silent collisions.
If discovery fails, install/repair from:
- `https://github.com/zenchantlive/beadboard`
## Session Runbook
1. Diagnose environment.
2. Confirm preflight/discovery.
3. Establish session identity.
4. Read memory + ready work.
5. Claim bead with assignee.
6. Execute and coordinate via events.
7. Run verification gates.
8. Publish evidence and close.
9. Perform memory review.
## Core Commands
```bash
& "$env:BB_REPO\bb.ps1" agent reserve --agent <agent-id> --scope "src/lib/*" --bead <bead-id>
bd update <bead-id> --status in_progress --claim
# Diagnostics and discovery
node skills/beadboard-driver/scripts/diagnose-env.mjs
node skills/beadboard-driver/scripts/session-preflight.mjs
node skills/beadboard-driver/scripts/resolve-bb.mjs
# Ensure project context exists in the target repository
node skills/beadboard-driver/scripts/ensure-project-context.mjs --project-root <repo>
# Identity helper
node skills/beadboard-driver/scripts/generate-agent-name.mjs
# Closeout evidence envelope
node skills/beadboard-driver/scripts/readiness-report.mjs --checks '<json>' --artifacts '<json>'
# Safe self-healing (dry-run default)
node skills/beadboard-driver/scripts/heal-common-issues.mjs --project-root <repo>
node skills/beadboard-driver/scripts/heal-common-issues.mjs --project-root <repo> --apply --fix-git-index-lock
```
3. **Physical Change -> Contextual Lookup**:
If you encounter uncommitted changes in a file you didn't personally edit: **STOP and Query**.
## Bead Lifecycle (Minimum Contract)
```bash
& "$env:BB_REPO\bb.ps1" agent status --agent <agent-id>
& "$env:BB_REPO\bb.ps1" agent inbox --agent <agent-id> --state unread
# Read context
bd show <memory-or-task-id>
bd ready
# Claim explicitly
bd update <bead-id> --status in_progress --assignee <agent-bead-id>
# Record evidence and close
bd update <bead-id> --notes "<commands + outputs>"
bd close <bead-id> --reason "<completed outcome>"
```
4. **Explain Deltas**:
Send high-fidelity signals when you hit milestones or incursions.
## Use-The-Right-Doc Map
### `references/memory-system.md`
Use when you need to query/apply/create canonical memory, validate provenance, or decide whether a lesson belongs in memory vs task notes.
### `references/coord-events-sessions-ack.md`
Use when youre coordinating handoffs/blockers/incursions and need correct inbox/read/ack behavior.
### `references/session-lifecycle.md`
Use for end-to-end session choreography and closeout hygiene.
### `references/archetypes-templates-swarms.md`
Use when choosing team shape, role boundaries, and swarm ownership patterns.
### `references/missions-realtime.md`
Use when assigning work and troubleshooting stale/live-update behavior from mission/event flow.
### `references/command-matrix.md`
Use when you need exact command surfaces and argument shape.
### `references/failure-modes.md`
Use when preflight/discovery/coordination fails and you need deterministic recovery.
## Project Context Template
The skill ships a source template file: `project.template.md`.
Runtime contract:
- Agents should use `<target-repo>/project.md` for project context.
- If `<target-repo>/project.md` is missing, create it from `project.template.md`.
- If `<target-repo>/project.md` already exists, do not overwrite it.
Helper command:
```bash
& "$env:BB_REPO\bb.ps1" agent send --from <agent-id> --to <peer> --bead <bead-id> --category INFO --subject "Patched parser.ts for UI sync" --body "..."
node skills/beadboard-driver/scripts/ensure-project-context.mjs --project-root <repo>
```
5. **Liveness Maintenance**:
Liveness is **Passive**. Any `bb agent` command extends your lease. Use `activity-lease` if you haven't run a command in > 10 minutes.
## Tests
Skill-local contracts:
- `skills/beadboard-driver/tests/run-tests.mjs`
- `skills/beadboard-driver/tests/*.contract.test.mjs`
Repo-level coverage:
- `tests/skills/beadboard-driver/*.test.ts`
## Verification Gates
```bash
& "$env:BB_REPO\bb.ps1" agent activity-lease --agent <agent-id> --json
npm run typecheck
npm run lint
npm run test
```
6. **Closeout Evidence**:
```bash
node skills/beadboard-driver/scripts/readiness-report.mjs --checks '[{"name":"typecheck","ok":true}]' --artifacts '[{"path":"artifacts/final.png","required":true}]'
bd close <bead-id> --reason "..."
```
If failures are outside your scope, cite exact failing files/tests and continue transparently.
## Identity & Adoption Policy
## Bottom Line
- **Uniqueness**: Create one unique `adjective-noun` identity per session unless adopting.
- **Adoption Guardrails**: Adoption is ONLY allowed if uncommitted changes exist in the scope OR you own an `in_progress` bead.
- **Audit**: Every adoption triggers a `RESUME` event in the audit feed.
## Activity Lease (Parking Permit)
- **Active (0-15m)**: Lease is valid. You are protected from takeover.
- **Stale (15-30m)**: Lease expired. Others can takeover with `--takeover-stale`.
- **Evicted (30m+)**: Lease dead. Others should takeover and archive your reservation.
- **Idle (60m+)**: Ghost state. You are considered gone.
## Red Flags - STOP and Start Over
- **Silent Incursion**: Editing a reserved file without sending an `INFO` message.
- **Identity Reuse**: Reusing an agent ID from a previous session without an adoption handshake.
- **Mocking**: Implementing mocks instead of coordinating with the domain owner.
- **Terminal Pop-ups**: Spawning background workers that disrupt the user's desktop.
## References
- Command and argument contracts: `references/command-matrix.md`
- End-to-end session choreography: `references/session-lifecycle.md`
- Protocol Specification: `docs/protocols/operative-protocol-v1.md`
This skill is the bridge between fast autonomous execution and human operator trust. Use it when speed matters, but coordination quality matters more.

View file

@ -1,4 +1,4 @@
interface:
display_name: "Beadboard Driver"
short_description: "Safe bd+bb agent workflow orchestration"
default_prompt: "Use Beadboard Driver to resolve bb path, register a unique session agent, coordinate via bb agent commands, and produce verification-backed closeout notes."
short_description: "BeadBoard control-plane workflow for agents in external repos"
default_prompt: "Use BeadBoard Driver v4 to run evidence-backed bd workflow in the target repo while the user orchestrates via BeadBoard UI; do not mutate project scope, use coordination events/inbox acks, and publish verification-backed closeout notes."

View file

@ -0,0 +1,85 @@
# Project Driver Template
Use this file to define project-specific operating notes for agents using the BeadBoard Driver skill.
## Project Identity
- Project name:
- Repository root:
- Primary language/runtime:
- Primary package manager:
## BeadBoard Relationship
- BeadBoard host/UI location:
- Project registration identifier (if used):
- Notes about how this project appears in BeadBoard UI:
## Scope and Authority
- User controls project scope selection in BeadBoard UI.
- Agents do not change scope.
- Agent execution context in this repo:
## Command Baseline
- Install command:
- Build command:
- Typecheck command:
- Lint command:
- Test command:
- Smoke command (optional):
## Verification Policy Overrides
- Required gates for this project:
- Known slow gates and timeout guidance:
- Evidence format expected in bead notes:
## Environment Constraints
- OS/platform expectations:
- Required environment variables:
- Secrets handling guidance:
- Known path/shell quirks:
## Known Workarounds
Document only stable, repeatable workarounds.
1. Trigger:
- Symptom:
- Workaround:
- Verification:
- Owner:
2. Trigger:
- Symptom:
- Workaround:
- Verification:
- Owner:
## Coordination Defaults
- Default role/archetype mapping used by this project:
- Default handoff style:
- Blocker escalation policy:
- Ack expectations for blocker/handoff messages:
## Safety Guardrails
- Forbidden commands/actions for this repo:
- Files/paths requiring explicit reservation before edit:
- External systems that require human approval:
## Session Closeout Checklist
- [ ] Bead status/assignee updated
- [ ] Verification commands executed and recorded
- [ ] Artifacts attached/linked
- [ ] Memory review performed
- [ ] Follow-up beads created (if needed)
## Change Log
- YYYY-MM-DD: Initial project template completed.

View file

@ -0,0 +1,96 @@
# Archetypes, Templates, and Swarms
## Purpose
Define reusable team structure for multi-agent work so assignments are predictable, auditable, and easy for users to orchestrate from BeadBoard.
## Core Principle
Archetypes and templates define team composition. Missions define task execution.
Keep these concerns separate.
## Archetypes (Role Contracts)
An archetype is a role with clear responsibilities and deliverable expectations.
Baseline archetypes:
- `coder`: implements scoped changes and provides evidence.
- `reviewer`: validates quality, regressions, and acceptance criteria.
- `writer`: maintains user-facing docs, memory docs, and operator notes.
Optional archetypes may exist per project, but every archetype should specify:
- primary responsibilities,
- quality gates,
- handoff inputs/outputs,
- escalation triggers.
## Team Templates (Composition Contracts)
A template is a named role composition for repeatable work patterns.
Examples:
- Fast lane: `coder + reviewer`
- Documentation lane: `writer + reviewer`
- Parallel lane: `orchestrator + coder + reviewer + writer`
Template quality rules:
- keep composition minimal,
- avoid duplicate authority,
- define ownership boundaries,
- define expected handoff order.
## Swarms (Runtime Team Instances)
A swarm is a live team instance operating on specific beads/epics.
Lifecycle:
1. Create swarm instance from a template or manual composition.
2. Join agents into explicit roles.
3. Assign beads with ownership.
4. Coordinate via events and inbox.
5. Leave or close swarm cleanly when complete.
## Command Surface (Representative)
Use your environment's swarm commands to manage lifecycle.
Expected operations:
- create swarm
- list/show swarm
- join swarm with role
- leave swarm
- close swarm
All swarm actions should produce observable state changes in BeadBoard views.
## Ownership Rules
- Every in-progress bead should have one clear assignee.
- Swarms may collaborate on an epic, but each bead needs an explicit owner.
- Multi-agent edits require reservation and coordination signals.
## User Orchestration Relationship
Users control orchestration from BeadBoard UI:
- choose team shape/template,
- assign or reassign roles,
- intervene on blockers,
- monitor throughput and liveness.
Agents execute according to assigned role and bead ownership.
## Anti-Patterns
- Role ambiguity (multiple agents assuming same responsibility).
- Oversized swarms with no clear ownership boundaries.
- Using templates as mission definitions.
- Running unassigned parallel work with no bead claim.
- Treating swarm closure as optional housekeeping.

View file

@ -36,3 +36,6 @@
- `node skills/beadboard-driver/scripts/session-preflight.mjs`
- `node skills/beadboard-driver/scripts/generate-agent-name.mjs`
- `node skills/beadboard-driver/scripts/readiness-report.mjs --checks <json> --artifacts <json>`
- `node skills/beadboard-driver/scripts/diagnose-env.mjs`
- `node skills/beadboard-driver/scripts/heal-common-issues.mjs [--project-root <path>] [--apply] [--fix-git-index-lock]`
- `node skills/beadboard-driver/scripts/ensure-project-context.mjs [--project-root <path>]`

View file

@ -0,0 +1,121 @@
# Coordination Events, Sessions, and Acknowledgment
## Purpose
Define how agents communicate status, blockers, incursions, and handoffs in a machine-readable way that BeadBoard can render and users can act on.
## Operating Model
- Agent works in a target repository.
- User watches and orchestrates from BeadBoard UI.
- Agent communication must flow through coordination events and inbox state transitions, not ad-hoc notes.
## Event Categories
Use explicit categories with clear intent:
- `HANDOFF`: transfer ownership or next action.
- `BLOCKED`: explicit dependency or missing input.
- `RESUME`: adoption/resumption event.
- `INFO`: milestone or important context.
- `INCURSION`: overlap/collision signal for reserved scope.
## Session Stream Expectations
Session feeds should be audit-friendly:
- Every coordination event has sender, recipient/system target, bead id, and timestamp.
- `INCURSION` and `RESUME` are first-class timeline rows, not hidden diagnostics.
- Events should be understandable by humans without reading implementation code.
## Message Lifecycle
Inbox state machine:
1. `unread` when message is delivered.
2. `read` when recipient opens/reads message.
3. `acked` when recipient explicitly acknowledges.
Required behavior:
- Only recipient may ack.
- Acks are explicit, not implied by read.
- Blocker and handoff flows should request ack when coordination certainty is required.
## Recommended Command Patterns
Send structured coordination event:
```bash
bb agent send \
--from <agent-id> \
--to <peer-agent-id> \
--bead <bead-id> \
--category <HANDOFF|BLOCKED|RESUME|INFO|INCURSION> \
--subject "<short summary>" \
--body "<actionable details>"
```
Read inbox for current bead/session work:
```bash
bb agent inbox --agent <agent-id> --state unread --bead <bead-id>
bb agent read --agent <agent-id> --message <message-id>
bb agent ack --agent <agent-id> --message <message-id>
```
## Coordination Contracts
### Handoff
A `HANDOFF` should include:
- what is done,
- what remains,
- concrete next action,
- whether ack is required.
### Blocked
A `BLOCKED` should include:
- blocker description,
- requested action,
- urgency,
- ack requirement.
### Incursion
An `INCURSION` should include:
- overlap kind (`exact` or `partial`),
- owner identity,
- incoming identity,
- owner liveness,
- resolution hint.
### Resume
A `RESUME` should include:
- resume reason,
- prior session identity,
- adopted identity,
- evidence summary for safe adoption.
## UX Alignment
Session UI should map event semantics to plain-language actions:
- Handoff label: "Passed to"
- Blocked label: "Needs input"
- Read action: "Seen"
- Ack action: "Accepted"
## Anti-Patterns
- Using comments instead of coordination events for handoffs.
- Silent reservation collisions with no `INCURSION`/`INFO` signal.
- Treating read as ack.
- Sending vague events with no actionable payload.
- Closing a blocked bead without tracking unblock communication.

View file

@ -38,3 +38,10 @@
- Do not write `.beads/issues.jsonl` directly.
- Do not close beads without verification evidence.
- Do not bypass `BB_REPO` when it is set but invalid; fix it explicitly.
## Local Environment Repair Signals
- `GIT_INDEX_LOCK_PRESENT`: stale git lock can block local operations.
- Recovery:
- confirm no active git process is using the repository,
- run `node skills/beadboard-driver/scripts/heal-common-issues.mjs --project-root <repo> --apply --fix-git-index-lock`.

View file

@ -0,0 +1,110 @@
# Memory System
## Purpose
Use BeadBoard memory to preserve reusable operating rules across sessions.
Memory is tracked in `bd` decision beads, not markdown notes. Task notes are for local execution context; canonical memory is for reusable rules.
## Execution Context
- Agents usually run in a target project repository, not the BeadBoard repository.
- Project scope is controlled by the user in the BeadBoard UI.
- Agents do not select or mutate project scope.
## Core Objects
- Anchor: domain parent bead (for example architecture, workflow, agent ops, reliability).
- Canonical memory: `type=decision` bead with memory labels.
- Provenance links: relations from memory to source evidence beads.
## Canonical Memory Contract
Create canonical memory only when the rule is reusable.
Required labels:
- `mem-canonical`
- `mem-hard` or `mem-soft`
- `memory`
- domain label such as `memory-agent`, `memory-arch`, `memory-workflow`, `memory-reliability`, `memory-ui`
Required description sections:
- `Scope:`
- `Out of Scope:`
- `Rule:`
- `Rationale:`
- `Failure Mode:`
Required acceptance style:
- Given/When/Then invariant
- Verification commands
## Workflow
1. Query existing memory first.
2. Validate the memory provenance before relying on it.
3. Apply existing canonical memory to current task design.
4. If a new reusable rule appears, create canonical memory.
5. Link anchor, evidence, and related work with `bd dep relate`.
6. Ratify by closing the memory bead once complete.
7. For changes to an existing rule, supersede; do not rewrite history.
## Query and Validation Commands
```bash
bd query "type=decision label:mem-canonical"
bd show <memory-id>
bd dep list <memory-id>
```
Interpretation checklist:
- Is the memory closed and canonical?
- Are provenance links present (2-5 evidence beads preferred)?
- Is the domain anchor relationship present?
## Create and Index Canonical Memory
```bash
bd create --title="[MEMORY][<DOMAIN>][HARD|SOFT] <rule sentence>" \
--description="Scope: ...\nOut of Scope: ...\nRule: ...\nRationale: ...\nFailure Mode: ..." \
--type=decision --priority=1 \
--label="mem-canonical,mem-hard,memory,memory-<domain>"
bd dep relate <anchor-id> <memory-id>
bd dep relate <memory-id> <source-bead-id>
```
Use `mem-soft` when the rule is guidance and `mem-hard` when it is non-negotiable.
## Evolve Memory Safely
Use supersession when changing canonical rules:
```bash
bd supersede <old-memory-id> --with <new-memory-id>
```
Do not edit historical memory beads to represent new policy.
## Noise Budget
Apply memory sparingly per active task:
- 3-7 related memory nodes
- 0-2 blocker contracts
- 1 primary anchor domain per canonical memory
- 2-5 source-bead provenance links
If the lesson is not reusable, record it in task notes instead of creating memory.
## Anti-Patterns
- Writing policy in ad-hoc markdown only.
- Using blocker edges for memory indexing.
- Creating duplicate canonical memory for the same rule.
- Creating memory for one-off incidents without recurrence.
- Claiming memory-backed completion without verification evidence.

View file

@ -0,0 +1,96 @@
# Missions and Realtime
## Purpose
Define how work assignments (missions) and realtime updates should behave so users can orchestrate external-repo execution from BeadBoard with confidence.
## Mission Model
A mission is an execution assignment bound to clear ownership and expected outputs.
Mission essentials:
- bead/epic scope,
- assigned owner,
- expected deliverable,
- dependency awareness,
- handoff path.
## Assignment Rules
- One active owner per bead-level mission.
- Multi-agent support is achieved through parallel missions, not shared ambiguous ownership.
- Mission assignment must be visible in BeadBoard and reflected in bead assignee/status fields.
## Mission Topology
Missions should align with dependency graph semantics:
- dependencies model execution order,
- independent missions can run in parallel,
- blocked missions must not be represented as ready work.
When topology changes, update bead dependency links first, then assignment communication.
## Realtime Contract
Realtime is the user visibility layer.
Expected sources:
- bead status updates,
- coordination events,
- reservation/lease changes,
- watcher/SSE refresh signals.
Expected outcomes:
- UI updates without manual refresh,
- consistent state across social/graph/session surfaces,
- event timeline continuity for audits.
## SSE/Event Behavior
Realtime streams should provide:
- monotonic event ids where supported,
- heartbeat behavior for long-lived connections,
- resilience to brief write bursts and file-watch jitter,
- eventual consistency with bead source of truth.
If stale-state is suspected, triage in this order:
1. Source-of-truth parity.
2. Read-path validation.
3. Watcher input coverage.
4. Event emission/subscription path.
## Agent Responsibilities
Agents must:
- emit meaningful coordination events during mission lifecycle,
- keep bead status and assignee current,
- provide verification evidence before close,
- avoid implicit/unlogged handoffs.
Agents must not:
- change BeadBoard UI project scope,
- rely on local assumptions not visible in event/state outputs.
## User Responsibilities
Users orchestrate control-plane actions in BeadBoard UI:
- scope selection,
- priority/assignment changes,
- intervention on blocked missions,
- monitoring mission and realtime health.
## Anti-Patterns
- Mission start without bead claim/assignee update.
- Hidden handoffs outside coordination events.
- Treating stale UI as resolved without parity checks.
- Closing missions without verification evidence.

View file

@ -2,10 +2,13 @@
## 1) Start Session
1. Run preflight.
2. Resolve bb path and confirm `bd` availability.
3. Generate unique session agent name.
4. Register agent identity.
1. Run environment diagnosis.
2. Run preflight.
3. Resolve bb path and confirm `bd` availability.
4. Generate unique session agent name.
5. Register agent identity.
6. Confirm you are operating in the assigned target repository.
7. Do not change project scope (scope is user-controlled in BeadBoard UI).
## 2) Pick and Claim Work

View file

@ -0,0 +1,79 @@
#!/usr/bin/env node
import fs from 'node:fs/promises';
import path from 'node:path';
import { findCommandInPath, resolveBbPath } from './lib/driver-lib.mjs';
async function pathExists(filePath) {
try {
await fs.access(filePath);
return true;
} catch {
return false;
}
}
async function main() {
const timestamp = new Date().toISOString();
const cwd = process.cwd();
const gitIndexLock = path.join(cwd, '.git', 'index.lock');
const findings = [];
const recommendations = [];
const bdPath = await findCommandInPath('bd');
const bb = await resolveBbPath();
const hasGitIndexLock = await pathExists(gitIndexLock);
if (!bdPath) {
findings.push({
code: 'BD_NOT_FOUND',
severity: 'high',
message: 'bd command not found in PATH.',
});
recommendations.push(
'Install BeadBoard tooling from https://github.com/zenchantlive/beadboard or add bd executable directory to PATH.',
);
}
if (!bb.ok) {
findings.push({
code: 'BB_NOT_FOUND',
severity: 'high',
message: bb.reason,
});
if (bb.remediation) {
recommendations.push(bb.remediation);
}
}
if (hasGitIndexLock) {
findings.push({
code: 'GIT_INDEX_LOCK_PRESENT',
severity: 'medium',
message: `Potential stale git lock detected at ${gitIndexLock}`,
});
recommendations.push('Run heal-common-issues.mjs with --apply --fix-git-index-lock if no git process is active.');
}
const payload = {
ok: findings.filter((item) => item.severity === 'high').length === 0,
timestamp,
environment: {
cwd,
project_root: cwd,
platform: process.platform,
node_version: process.version,
},
tools: {
bd: { available: Boolean(bdPath), path: bdPath || null },
bb,
},
findings,
recommendations,
};
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
}
void main();

View file

@ -0,0 +1,85 @@
#!/usr/bin/env node
import fs from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
function parseArgs(argv) {
const args = new Set();
const values = {};
for (let i = 0; i < argv.length; i += 1) {
const token = argv[i];
if (!token.startsWith('--')) {
continue;
}
const key = token.slice(2);
const next = argv[i + 1];
if (!next || next.startsWith('--')) {
args.add(key);
continue;
}
values[key] = next;
i += 1;
}
return { args, values };
}
async function exists(filePath) {
try {
await fs.access(filePath);
return true;
} catch {
return false;
}
}
async function main() {
const { values } = parseArgs(process.argv.slice(2));
const projectRoot = values['project-root'] ? path.resolve(values['project-root']) : process.cwd();
const thisFile = fileURLToPath(import.meta.url);
const skillRoot = path.resolve(path.dirname(thisFile), '..');
const templatePath = path.join(skillRoot, 'project.template.md');
const targetPath = path.join(projectRoot, 'project.md');
const targetExists = await exists(targetPath);
if (targetExists) {
process.stdout.write(
`${JSON.stringify(
{
ok: true,
created: false,
used_existing: true,
project_root: projectRoot,
target_path: targetPath,
template_path: templatePath,
},
null,
2,
)}\n`,
);
return;
}
const template = await fs.readFile(templatePath, 'utf8');
await fs.writeFile(targetPath, template, 'utf8');
process.stdout.write(
`${JSON.stringify(
{
ok: true,
created: true,
used_existing: false,
project_root: projectRoot,
target_path: targetPath,
template_path: templatePath,
},
null,
2,
)}\n`,
);
}
void main();

View file

@ -0,0 +1,104 @@
#!/usr/bin/env node
import fs from 'node:fs/promises';
import path from 'node:path';
function parseArgs(argv) {
const args = new Set();
const values = {};
for (let i = 0; i < argv.length; i += 1) {
const token = argv[i];
if (!token.startsWith('--')) {
continue;
}
const key = token.slice(2);
const next = argv[i + 1];
if (!next || next.startsWith('--')) {
args.add(key);
continue;
}
values[key] = next;
i += 1;
}
return { args, values };
}
async function pathExists(filePath) {
try {
await fs.access(filePath);
return true;
} catch {
return false;
}
}
async function removeIfExists(filePath) {
if (!(await pathExists(filePath))) {
return false;
}
await fs.rm(filePath, { force: true });
return true;
}
async function main() {
const { args, values } = parseArgs(process.argv.slice(2));
const apply = args.has('apply');
const fixGitIndexLock = args.has('fix-git-index-lock');
const projectRoot = values['project-root'] ? path.resolve(values['project-root']) : process.cwd();
const actions = [];
const warnings = [];
const lockPath = path.join(projectRoot, '.git', 'index.lock');
const lockExists = await pathExists(lockPath);
if (fixGitIndexLock && lockExists) {
if (apply) {
const removed = await removeIfExists(lockPath);
actions.push({
id: 'fix-git-index-lock',
attempted: true,
applied: removed,
target: lockPath,
});
} else {
actions.push({
id: 'fix-git-index-lock',
attempted: true,
applied: false,
target: lockPath,
});
warnings.push('Dry-run mode enabled. Re-run with --apply to perform fixes.');
}
} else if (fixGitIndexLock && !lockExists) {
actions.push({
id: 'fix-git-index-lock',
attempted: true,
applied: false,
target: lockPath,
note: 'No index.lock found.',
});
}
if (!fixGitIndexLock && lockExists) {
warnings.push('Stale git index.lock detected. Use --fix-git-index-lock to target it.');
}
process.stdout.write(
`${JSON.stringify(
{
ok: true,
mode: apply ? 'apply' : 'dry-run',
project_root: projectRoot,
actions,
warnings,
},
null,
2,
)}\n`,
);
}
void main();

View file

@ -0,0 +1,24 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import path from 'node:path';
import { execFile } from 'node:child_process';
import { promisify } from 'node:util';
import { fileURLToPath } from 'node:url';
const execFileAsync = promisify(execFile);
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const scriptPath = path.resolve(__dirname, '..', 'scripts', 'diagnose-env.mjs');
test('diagnose-env contract: returns stable schema', async () => {
const { stdout } = await execFileAsync(process.execPath, [scriptPath], {
env: { ...process.env, PATH: '' },
});
const result = JSON.parse(stdout);
assert.equal(typeof result.ok, 'boolean');
assert.equal(typeof result.timestamp, 'string');
assert.equal(result.environment !== null, true);
assert.equal(Array.isArray(result.findings), true);
assert.equal(Array.isArray(result.recommendations), true);
});

View file

@ -0,0 +1,47 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { execFile } from 'node:child_process';
import { promisify } from 'node:util';
import { fileURLToPath } from 'node:url';
const execFileAsync = promisify(execFile);
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const scriptPath = path.resolve(__dirname, '..', 'scripts', 'ensure-project-context.mjs');
test('ensure-project-context creates project.md when missing', async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'bb-skill-project-context-'));
try {
const { stdout } = await execFileAsync(process.execPath, [scriptPath, '--project-root', root]);
const result = JSON.parse(stdout);
const content = await fs.readFile(path.join(root, 'project.md'), 'utf8');
assert.equal(result.ok, true);
assert.equal(result.created, true);
assert.match(content, /Project Driver Template/);
} finally {
await fs.rm(root, { recursive: true, force: true });
}
});
test('ensure-project-context preserves existing project.md', async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'bb-skill-project-context-'));
try {
const target = path.join(root, 'project.md');
await fs.writeFile(target, '# existing\n', 'utf8');
const { stdout } = await execFileAsync(process.execPath, [scriptPath, '--project-root', root]);
const result = JSON.parse(stdout);
const content = await fs.readFile(target, 'utf8');
assert.equal(result.ok, true);
assert.equal(result.created, false);
assert.equal(result.used_existing, true);
assert.equal(content, '# existing\n');
} finally {
await fs.rm(root, { recursive: true, force: true });
}
});

View file

@ -0,0 +1,69 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { execFile } from 'node:child_process';
import { promisify } from 'node:util';
import { fileURLToPath } from 'node:url';
const execFileAsync = promisify(execFile);
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const scriptPath = path.resolve(__dirname, '..', 'scripts', 'heal-common-issues.mjs');
test('heal-common-issues contract: dry-run does not mutate git index.lock', async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'bb-skill-heal-'));
try {
const lockDir = path.join(root, '.git');
const lockPath = path.join(lockDir, 'index.lock');
await fs.mkdir(lockDir, { recursive: true });
await fs.writeFile(lockPath, 'locked', 'utf8');
const { stdout } = await execFileAsync(process.execPath, [scriptPath, '--project-root', root], {
env: process.env,
});
const result = JSON.parse(stdout);
const lockStillExists = await fs
.access(lockPath)
.then(() => true)
.catch(() => false);
assert.equal(result.ok, true);
assert.equal(result.mode, 'dry-run');
assert.equal(lockStillExists, true);
} finally {
await fs.rm(root, { recursive: true, force: true });
}
});
test('heal-common-issues contract: apply removes stale git index.lock when opted in', async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'bb-skill-heal-'));
try {
const lockDir = path.join(root, '.git');
const lockPath = path.join(lockDir, 'index.lock');
await fs.mkdir(lockDir, { recursive: true });
await fs.writeFile(lockPath, 'locked', 'utf8');
const { stdout } = await execFileAsync(
process.execPath,
[scriptPath, '--project-root', root, '--apply', '--fix-git-index-lock'],
{
env: process.env,
},
);
const result = JSON.parse(stdout);
const lockStillExists = await fs
.access(lockPath)
.then(() => true)
.catch(() => false);
assert.equal(result.ok, true);
assert.equal(result.mode, 'apply');
assert.equal(lockStillExists, false);
} finally {
await fs.rm(root, { recursive: true, force: true });
}
});

View file

@ -0,0 +1,35 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { execFile } from 'node:child_process';
import { promisify } from 'node:util';
import { fileURLToPath } from 'node:url';
const execFileAsync = promisify(execFile);
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const scriptPath = path.resolve(__dirname, '..', 'scripts', 'readiness-report.mjs');
test('readiness-report contract: returns ready true for passing checks and present artifacts', async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'bb-skill-readiness-'));
try {
const artifact = path.join(root, 'artifact.txt');
await fs.writeFile(artifact, 'ok', 'utf8');
const checks = JSON.stringify([
{ name: 'typecheck', ok: true, details: 'pass' },
{ name: 'lint', ok: true, details: 'pass' },
]);
const artifacts = JSON.stringify([{ path: artifact, required: true }]);
const { stdout } = await execFileAsync(process.execPath, [scriptPath, '--checks', checks, '--artifacts', artifacts]);
const result = JSON.parse(stdout);
assert.equal(result.ok, true);
assert.equal(result.summary.ready, true);
} finally {
await fs.rm(root, { recursive: true, force: true });
}
});

View file

@ -11,6 +11,10 @@ const tests = [
path.join(__dirname, 'resolve-bb.contract.test.mjs'),
path.join(__dirname, 'generate-agent-name.contract.test.mjs'),
path.join(__dirname, 'session-preflight.contract.test.mjs'),
path.join(__dirname, 'readiness-report.contract.test.mjs'),
path.join(__dirname, 'diagnose-env.contract.test.mjs'),
path.join(__dirname, 'heal-common-issues.contract.test.mjs'),
path.join(__dirname, 'ensure-project-context.contract.test.mjs'),
];
const child = spawn(process.execPath, ['--test', ...tests], {

View file

@ -26,10 +26,17 @@ test('session-preflight contract: succeeds with bd + BB_REPO', async () => {
try {
const repo = path.join(root, 'beadboard');
const toolsDir = path.join(root, 'tools');
const bdExecutable = process.platform === 'win32' ? 'bd.cmd' : 'bd';
const bdPath = path.join(toolsDir, bdExecutable);
await fs.mkdir(path.join(repo, 'tools'), { recursive: true });
await fs.mkdir(toolsDir, { recursive: true });
await fs.writeFile(path.join(repo, 'bb.ps1'), 'echo ok', 'utf8');
await fs.writeFile(path.join(toolsDir, 'bd.cmd'), '@echo off\r\necho beads\r\n', 'utf8');
if (process.platform === 'win32') {
await fs.writeFile(bdPath, '@echo off\r\necho beads\r\n', 'utf8');
} else {
await fs.writeFile(bdPath, '#!/usr/bin/env sh\necho beads\n', 'utf8');
await fs.chmod(bdPath, 0o755);
}
const { stdout } = await execFileAsync(process.execPath, [scriptPath], {
env: { ...process.env, PATH: toolsDir, BB_REPO: repo, BB_SKILL_HOME: path.join(root, 'home') },

1
skills/shadcn-ui Symbolic link
View file

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

File diff suppressed because it is too large Load diff

View file

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

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

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

@ -4,6 +4,8 @@ import { readIssuesForScope } from '../lib/aggregate-read';
import { resolveProjectScope } from '../lib/project-scope';
import { listProjects } from '../lib/registry';
export const dynamic = 'force-dynamic';
interface PageProps {
searchParams?: Promise<Record<string, string | string[] | undefined>>;
}

View file

@ -21,6 +21,7 @@ import { TaskCardGrid, type BlockerDetail } from './task-card-grid';
import { TaskDetailsDrawer } from './task-details-drawer';
import { DependencyFlowStrip } from './dependency-flow-strip';
import { GraphNodeCard, type GraphNodeData } from './graph-node-card';
import { OffsetEdge } from './offset-edge';
import { GraphSection } from './graph-section';
import { ProjectScopeControls } from '../shared/project-scope-controls';
import { WorkspaceHero } from '../shared/workspace-hero';
@ -31,6 +32,7 @@ import {
type GraphHopDepth,
analyzeBlockedChain,
detectDependencyCycles,
identifyTransitiveEdges,
} from '../../lib/graph-view';
import { buildBlockedByTree } from '../../lib/kanban';
import { type BeadIssue } from '../../lib/types';
@ -167,6 +169,10 @@ export function DependencyGraphPage({
}),
[issues, hideClosed],
);
const selectableEpics = useMemo(
() => epics.filter((epic) => (!hideClosed ? true : epic.status !== 'closed' && epic.status !== 'tombstone')),
[epics, hideClosed],
);
// --- Derived data: tasks grouped by parent epic ---
const tasksByEpic = useMemo(() => {
@ -226,18 +232,18 @@ export function DependencyGraphPage({
// --- Auto-select first epic if none selected ---
useEffect(() => {
if (epics.length === 0) {
if (selectableEpics.length === 0) {
if (selectedEpicId !== null) {
setSelectedEpicId(null);
}
return;
}
const hasSelectedEpic = selectedEpicId ? epics.some((epic) => epic.id === selectedEpicId) : false;
const hasSelectedEpic = selectedEpicId ? selectableEpics.some((epic) => epic.id === selectedEpicId) : false;
if (!hasSelectedEpic) {
setSelectedEpicId(epics[0].id);
setSelectedEpicId(selectableEpics[0].id);
}
}, [epics, selectedEpicId]);
}, [selectableEpics, selectedEpicId]);
useEffect(() => {
if (requestedTab === 'tasks' || requestedTab === 'dependencies') {
@ -247,9 +253,9 @@ export function DependencyGraphPage({
useEffect(() => {
if (!requestedEpicId) return;
if (!epics.some((epic) => epic.id === requestedEpicId)) return;
if (!selectableEpics.some((epic) => epic.id === requestedEpicId)) return;
setSelectedEpicId(requestedEpicId);
}, [epics, requestedEpicId]);
}, [selectableEpics, requestedEpicId]);
useEffect(() => {
if (!requestedTaskId) {
@ -272,7 +278,7 @@ export function DependencyGraphPage({
}, [issues, selectedId]);
// --- Derived: selected epic and its tasks ---
const selectedEpic = useMemo(() => epics.find((epic) => epic.id === selectedEpicId) ?? null, [epics, selectedEpicId]);
const selectedEpic = useMemo(() => selectableEpics.find((epic) => epic.id === selectedEpicId) ?? null, [selectableEpics, selectedEpicId]);
const projectLevelTasks = useMemo(
() =>
issues
@ -301,8 +307,8 @@ export function DependencyGraphPage({
}
// Last-resort fallback: if there are only epics, render epics as selectable items.
return epics.filter((epic) => (!hideClosed ? true : epic.status !== 'closed'));
}, [epics, hideClosed, projectLevelTasks, selectedEpic, tasksByEpic]);
return selectableEpics;
}, [projectLevelTasks, selectableEpics, selectedEpic, tasksByEpic]);
const selectedEpicHasChildren = useMemo(() => {
if (selectedEpic) {
@ -326,6 +332,9 @@ export function DependencyGraphPage({
// --- Graph model ---
const graphModel = useMemo(() => buildGraphModel(issues, { projectKey: projectRoot }), [issues, projectRoot]);
// --- Transitive edges (redundant blocks) ---
const transitiveEdges = useMemo(() => identifyTransitiveEdges(graphModel), [graphModel]);
// --- Signal map: blocker/blocks counts per issue ---
const signalById = useMemo(() => {
const map = new Map<string, { blockedBy: number; blocks: number }>();
@ -531,7 +540,7 @@ export function DependencyGraphPage({
blocks: signalById.get(issue.id)?.blocks ?? 0,
isActionable: actionableNodeIds.has(issue.id),
isCycleNode: cycleNodeIdSet.has(issue.id),
isDimmed: selectedId ? !chainNodeIds.has(issue.id) : false,
isDimmed: focusId ? !chainNodeIds.has(issue.id) : false,
blockerTooltipLines: externalBlockerNames.get(issue.id) ?? blockerTooltipMap.get(issue.id) ?? [],
labels: issue.labels,
},
@ -542,6 +551,63 @@ export function DependencyGraphPage({
}));
const visibleIds = new Set(baseNodes.map((node) => node.id));
// Use requestedTaskId from URL as the focus node for upstream/downstream highlighting.
// `selectedId` is local state that tracks click selection for the drawer,
// but it starts as null. The URL `task` param is what the user clicked in the graph
// (set by handleNodeSelect -> router.push). We use requestedTaskId here
// so that clicking a node - which updates the URL - also triggers edge color changes.
const focusId = requestedTaskId;
// --- Compute Upstream / Downstream Focus ---
const upstreamIds = new Set<string>();
const downstreamIds = new Set<string>();
if (focusId && visibleIds.has(focusId)) {
upstreamIds.add(focusId);
downstreamIds.add(focusId);
const outgoing = new Map<string, string[]>();
const incoming = new Map<string, string[]>();
for (const issue of issues) {
for (const dep of issue.dependencies) {
if (dep.type === 'blocks') {
const blocker = dep.target;
const blocked = issue.id;
if (!outgoing.has(blocker)) outgoing.set(blocker, []);
if (!incoming.has(blocked)) incoming.set(blocked, []);
outgoing.get(blocker)!.push(blocked);
incoming.get(blocked)!.push(blocker);
}
}
}
let queue = [focusId];
while (queue.length > 0) {
const curr = queue.shift()!;
for (const b of (incoming.get(curr) || [])) {
if (!upstreamIds.has(b)) {
upstreamIds.add(b);
queue.push(b);
}
}
}
queue = [focusId];
while (queue.length > 0) {
const curr = queue.shift()!;
for (const b of (outgoing.get(curr) || [])) {
if (!downstreamIds.has(b)) {
downstreamIds.add(b);
queue.push(b);
}
}
}
}
const graphEdges: Edge[] = [];
// Search ALL issues for blocking edges between visible nodes.
@ -550,25 +616,87 @@ export function DependencyGraphPage({
for (const issue of issues) {
for (const dep of issue.dependencies) {
// Both endpoints must be visible in the graph
if (!visibleIds.has(issue.id) && !visibleIds.has(dep.target)) continue;
if (!visibleIds.has(issue.id) || !visibleIds.has(dep.target)) continue;
// Only show blocking edges (skip parent, relates_to, etc.)
if (dep.type !== 'blocks') continue;
// Avoid self-loops
if (issue.id === dep.target) continue;
const edgeId = `${dep.target}:blocks:${issue.id}`;
const linkedToSelection = selectedId ? issue.id === selectedId || dep.target === selectedId : false;
const edgeId = `${dep.target}:blocks:${issue.id}`;
const sourceId = dep.target;
const targetId = issue.id;
const isUpstreamOfFocus = focusId ? upstreamIds.has(sourceId) && upstreamIds.has(targetId) : false;
const isDownstreamOfFocus = focusId ? downstreamIds.has(sourceId) && downstreamIds.has(targetId) : false;
const isDirectlyFocused = focusId ? sourceId === focusId || targetId === focusId : false;
let isUnrelated = false;
if (focusId) {
isUnrelated = !isUpstreamOfFocus && !isDownstreamOfFocus && !isDirectlyFocused;
}
const sourceNode = issues.find(i => i.id === sourceId);
const sourceStatus = sourceNode?.status || 'open';
const isTransitive = transitiveEdges.has(edgeId);
let stroke = '#3b82f6';
let strokeBg = 'rgba(59, 130, 246, 0.25)';
let dashArray: string | undefined = undefined;
let opacity = 0.8;
const isFocusedPath = isUpstreamOfFocus || isDownstreamOfFocus || isDirectlyFocused;
const isAnimated = isFocusedPath || sourceStatus === 'in_progress';
// Base Status Colors
if (sourceStatus === 'in_progress') {
stroke = '#fbbf24'; // Bright Amber
strokeBg = 'rgba(251, 191, 36, 0.25)';
} else if (sourceStatus === 'blocked') {
stroke = '#f43f5e'; // Rose/Red for deep block
strokeBg = 'rgba(244, 63, 94, 0.25)';
} else {
stroke = '#3b82f6'; // Blue for open/ready
strokeBg = 'rgba(59, 130, 246, 0.25)';
}
// Selection Focus Overrides
if (focusId) {
if (isUnrelated) {
stroke = '#1e293b'; // Super dim unrelated edges
strokeBg = 'transparent';
opacity = 0.15;
} else if (isUpstreamOfFocus || (isDirectlyFocused && targetId === focusId)) {
stroke = '#f59e0b'; // Amber -- "I am blocking you"
strokeBg = 'rgba(245, 158, 11, 0.35)';
opacity = 1;
} else if (isDownstreamOfFocus || (isDirectlyFocused && sourceId === focusId)) {
stroke = '#0ea5e9'; // Cyan -- "you are blocking me"
strokeBg = 'rgba(14, 165, 233, 0.35)';
opacity = 1;
}
}
// Transitive Styling
if (isTransitive) {
dashArray = '4 4';
if (!focusId || isUnrelated) {
stroke = '#334155';
strokeBg = 'rgba(51, 65, 85, 0.3)';
opacity = 0.4;
} else {
opacity = 0.6; // Keep focused color but make dashed/transparent
}
}
graphEdges.push({
id: edgeId,
source: dep.target,
target: issue.id,
className: linkedToSelection ? 'workflow-edge-selected' : 'workflow-edge-muted',
animated: linkedToSelection,
source: sourceId,
target: targetId,
className: isFocusedPath ? 'workflow-edge-selected' : 'workflow-edge-muted',
animated: isAnimated,
label: 'BLOCKS',
labelStyle: {
fill: linkedToSelection ? '#e2e8f0' : '#cbd5e1',
fill: isFocusedPath ? '#e2e8f0' : '#cbd5e1',
fontSize: 10,
fontWeight: 700,
letterSpacing: '0.08em',
@ -577,25 +705,50 @@ export function DependencyGraphPage({
labelBgBorderRadius: 999,
labelBgStyle: {
fill: 'rgba(2, 6, 23, 0.92)',
stroke: linkedToSelection ? 'rgba(125, 211, 252, 0.35)' : 'rgba(251, 191, 36, 0.25)',
stroke: strokeBg,
strokeWidth: 1,
},
style: {
stroke: linkedToSelection ? '#7dd3fc' : '#fbbf24',
strokeWidth: linkedToSelection ? 2.8 : 2.1,
opacity: linkedToSelection ? 1 : 0.78,
stroke,
strokeWidth: isFocusedPath ? 2.8 : 2.1,
opacity,
strokeDasharray: dashArray,
},
markerEnd: { type: MarkerType.ArrowClosed, color: linkedToSelection ? '#7dd3fc' : '#fbbf24', width: 14, height: 14 },
markerEnd: { type: MarkerType.ArrowClosed, color: stroke, width: 14, height: 14 },
});
}
}
// --- Apply Offsets to Edge Data ---
// Count how many edges share the same source and target, or just
// group them by axis line to separate them visually.
const edgeGroups = new Map<string, Edge[]>();
for (const edge of graphEdges) {
// Create a normalized key roughly defining the segment direction
const key = [edge.source, edge.target].sort().join('-');
if (!edgeGroups.has(key)) edgeGroups.set(key, []);
edgeGroups.get(key)!.push(edge);
}
// Assign offsets based on index in their shared group.
for (const [unused_, groupEdges] of edgeGroups) {
if (groupEdges.length <= 1) continue;
const step = 8; // 8px offset per line
const totalSpread = (groupEdges.length - 1) * step;
let currentOffset = -(totalSpread / 2);
for (const edge of groupEdges) {
edge.data = { ...edge.data, offset: currentOffset };
currentOffset += step;
}
}
return {
nodes: layoutDagre(baseNodes, graphEdges),
edges: graphEdges,
};
}, [
hideClosed, issues, selectedEpicTasks, selectedId,
transitiveEdges, hideClosed, issues, selectedEpicTasks, requestedTaskId,
signalById, actionableNodeIds, cycleNodeIdSet,
chainNodeIds, blockerTooltipMap, externalBlockerNames,
]);
@ -607,6 +760,13 @@ export function DependencyGraphPage({
[],
);
const edgeTypes = useMemo(
() => ({
offset: OffsetEdge,
}),
[]
);
// --- Handle node click in the graph (also opens detail drawer) ---
const handleFlowNodeClick: NodeMouseHandler = useCallback((_, node) => {
setSelectedId(node.id);
@ -714,7 +874,7 @@ export function DependencyGraphPage({
{/* Epic chip strip - shows titles, not just IDs */}
<div className="flex-1 min-w-0">
<EpicChipStrip
epics={epics}
epics={selectableEpics}
selectedEpicId={selectedEpicId}
beadCounts={beadCounts}
onSelect={setSelectedEpicId}
@ -826,7 +986,7 @@ export function DependencyGraphPage({
<h2 className="text-[10px] font-bold uppercase tracking-[0.2em] text-text-muted/70">1) Select Epic</h2>
<div className="mt-4">
<EpicChipStrip
epics={epics}
epics={selectableEpics}
selectedEpicId={selectedEpicId}
beadCounts={beadCounts}
onSelect={setSelectedEpicId}
@ -893,6 +1053,7 @@ export function DependencyGraphPage({
nodes={flowModel.nodes}
edges={flowModel.edges}
nodeTypes={nodeTypes}
edgeTypes={edgeTypes}
defaultEdgeOptions={defaultEdgeOptions}
onNodeClick={handleFlowNodeClick}
blockerAnalysis={blockerAnalysis}

View file

@ -20,6 +20,7 @@ interface GraphSectionProps {
edges: Edge[];
/** Map of custom node type names to their React components. */
nodeTypes: NodeTypes;
edgeTypes?: any;
/** Default edge rendering options. */
defaultEdgeOptions: {
type: 'smoothstep';
@ -43,6 +44,7 @@ export function GraphSection({
nodes,
edges,
nodeTypes,
edgeTypes,
defaultEdgeOptions,
onNodeClick,
blockerAnalysis,
@ -101,6 +103,7 @@ export function GraphSection({
nodes={nodes}
edges={edges}
nodeTypes={nodeTypes}
edgeTypes={edgeTypes}
nodesDraggable={false}
nodesConnectable={false}
elementsSelectable

View file

@ -0,0 +1,82 @@
import React from 'react';
import { BaseEdge, EdgeProps, getSmoothStepPath, EdgeLabelRenderer } from '@xyflow/react';
export function OffsetEdge(props: EdgeProps) {
const {
sourceX,
sourceY,
targetX,
targetY,
sourcePosition,
targetPosition,
style = {},
markerEnd,
data,
label,
labelStyle,
labelBgStyle,
labelBgPadding,
labelBgBorderRadius,
animated,
} = props;
// We can pass `offset` via the edge data. Positive or negative pixels.
const offset = data?.offset as number | undefined ?? 0;
// Apply offset to the Y axis for Left/Right layouts (horizontal edges)
// or to the X axis for Top/Bottom layouts (vertical edges).
// Assuming 'sourcePosition' dictates the primary flow direction.
let sx = sourceX;
let sy = sourceY;
let tx = targetX;
let ty = targetY;
if (sourcePosition === 'right' || sourcePosition === 'left') {
// Horizontal flow, offset the vertical axis (Y)
sy += offset;
ty += offset;
} else {
// Vertical flow, offset the horizontal axis (X)
sx += offset;
tx += offset;
}
const [edgePath, labelX, labelY] = getSmoothStepPath({
sourceX: sx,
sourceY: sy,
sourcePosition,
targetX: tx,
targetY: ty,
targetPosition,
// Optional: reduce the corner radius slightly for tighter clusters
borderRadius: 8,
});
return (
<>
<BaseEdge
path={edgePath}
markerEnd={markerEnd}
className={animated ? "animated-edge" : ""}
style={{ ...style, strokeDasharray: animated ? "5, 5" : "none" }}
/>
{label && (
<EdgeLabelRenderer>
<div
style={{
position: 'absolute',
transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`,
pointerEvents: 'all',
...(labelBgStyle as React.CSSProperties),
padding: Array.isArray(labelBgPadding) ? `${labelBgPadding[0]}px ${labelBgPadding[1]}px` : labelBgPadding,
borderRadius: labelBgBorderRadius,
}}
className="nodrag nopan"
>
<div style={labelStyle as React.CSSProperties}>{label}</div>
</div>
</EdgeLabelRenderer>
)}
</>
);
}

View file

@ -7,6 +7,7 @@ import { useUrlState, buildUrlParams } from '../../hooks/use-url-state';
import type { BeadIssue } from '../../lib/types';
import type { GraphHopDepth } from '../../lib/graph-view';
import { collectEpicDescendantIds } from '../../lib/epic-graph';
import { WorkflowGraph } from '../shared/workflow-graph';
import { WorkflowTabs, type WorkflowTab } from './workflow-tabs';
import { TaskCardGrid, type BlockerDetail } from './task-card-grid';
@ -75,11 +76,8 @@ export function SmartDag({
const displayBeads = useMemo(() => {
if (!epicId) return issues;
return issues.filter(issue => {
if (issue.issue_type === 'epic') return false;
const parent = issue.dependencies.find(d => d.type === 'parent');
return parent?.target === epicId;
});
const descendantIds = collectEpicDescendantIds(issues, epicId);
return issues.filter((issue) => descendantIds.has(issue.id));
}, [issues, epicId]);
const {

View file

@ -41,6 +41,32 @@ interface EpicEntry {
latestTimestamp: string;
}
export function shouldHideEpicEntry(params: {
epicStatus: BeadIssue['status'];
matchedChildrenCount: number;
totalChildrenCount: number;
isSelected: boolean;
filters: LeftPanelFilters;
}): boolean {
const { epicStatus, matchedChildrenCount, totalChildrenCount, isSelected, filters } = params;
const hasTaskFilters =
filters.query.trim().length > 0 ||
filters.status !== 'all' ||
filters.priority !== 'all' ||
filters.preset !== 'all';
const epicClosed = epicStatus === 'closed' || epicStatus === 'tombstone';
const noVisibleChildren = matchedChildrenCount === 0 && totalChildrenCount > 0;
const hiddenByTaskFilters = hasTaskFilters && noVisibleChildren;
const hiddenByHideClosed = filters.hideClosed && noVisibleChildren;
const hiddenByEpicClosed = filters.hideClosed && epicClosed;
if (hiddenByEpicClosed) {
return true;
}
return !isSelected && (hiddenByTaskFilters || hiddenByHideClosed);
}
function mapStatus(task: BeadIssue): LeftPanelStatusFilter {
if (task.status === 'open') return 'ready';
if (task.status === 'in_progress') return 'in_progress';
@ -179,13 +205,6 @@ export function LeftPanel({ issues, selectedEpicId, onEpicSelect, onEpicEdit, fi
const entries = useMemo(() => buildEntries(issues), [issues]);
const [expanded, setExpanded] = useState<Record<string, boolean>>({});
const hasActiveFilters =
filters.query.trim().length > 0 ||
filters.status !== 'all' ||
filters.priority !== 'all' ||
filters.preset !== 'all' ||
filters.hideClosed;
const views: Array<{ id: ViewType; label: string }> = [
{ id: 'social', label: 'Social' },
{ id: 'graph', label: 'Graph' },
@ -324,7 +343,13 @@ export function LeftPanel({ issues, selectedEpicId, onEpicSelect, onEpicEdit, fi
const laneColor = blockedCount > 0 ? 'var(--accent-danger)' : activeCount > 0 ? 'var(--accent-warning)' : 'var(--accent-success)';
const rowBackground = rowTone(entry);
if (matchedChildren.length === 0 && hasActiveFilters && !isSelected) {
if (shouldHideEpicEntry({
epicStatus: epic.status,
matchedChildrenCount: matchedChildren.length,
totalChildrenCount: total,
isSelected,
filters,
})) {
return null;
}

View file

@ -161,6 +161,19 @@ export function UnifiedShell({
const drawerId = taskId || swarmId || epicId || '';
const selectedItem = selectedEpic ?? selectedIssue;
useEffect(() => {
if (!filters.hideClosed || !epicId) {
return;
}
const epic = issues.find((issue) => issue.id === epicId && issue.issue_type === 'epic');
if (!epic) {
return;
}
if (epic.status === 'closed' || epic.status === 'tombstone') {
setEpicId(null);
}
}, [filters.hideClosed, epicId, issues, setEpicId]);
// Panel resize hook
const { leftWidth, rightWidth, handleLeftResize, handleRightResize } = usePanelResize();

View file

@ -18,8 +18,11 @@ import { Maximize2 } from 'lucide-react';
import type { BeadIssue } from '../../lib/types';
import type { AgentArchetype } from '../../lib/types-swarm';
import { buildWorkflowEdges } from '../../lib/epic-graph';
import { useGraphAnalysis } from '../../hooks/use-graph-analysis';
import { identifyTransitiveEdges } from '../../lib/graph-view';
import { GraphNodeCard, type GraphNodeData } from '../graph/graph-node-card';
import { OffsetEdge } from '../graph/offset-edge';
export interface WorkflowGraphProps {
beads: BeadIssue[];
@ -92,9 +95,11 @@ function WorkflowGraphInner({
const { fitView } = useReactFlow();
const [layoutDirection, setLayoutDirection] = useState<LayoutDirection>('LR');
const [layoutDensity, setLayoutDensity] = useState<LayoutDensity>('normal');
const [showHierarchy, setShowHierarchy] = useState(true);
// Use the extracted hook for all graph analysis
const {
graphModel,
signalById,
cycleNodeIdSet,
actionableNodeIds,
@ -103,6 +108,8 @@ function WorkflowGraphInner({
chainNodeIds,
} = useGraphAnalysis(beads, 'workflow', selectedId);
const transitiveEdges = useMemo(() => identifyTransitiveEdges(graphModel), [graphModel]);
const flowModel = useMemo(() => {
const visibleBeads = beads.filter((issue) => (!hideClosed ? true : issue.status !== 'closed'));
@ -155,52 +162,128 @@ function WorkflowGraphInner({
});
const visibleIds = new Set(baseNodes.map((node) => node.id));
const graphEdges: Edge[] = [];
const edgeDescriptors = buildWorkflowEdges({
issues: beads,
visibleIds,
selectedId: selectedId ?? null,
includeHierarchy: showHierarchy,
});
for (const issue of beads) {
for (const dep of issue.dependencies) {
if (!visibleIds.has(issue.id) && !visibleIds.has(dep.target)) continue;
if (!visibleIds.has(issue.id) || !visibleIds.has(dep.target)) continue;
if (dep.type !== 'blocks') continue;
if (issue.id === dep.target) continue;
const graphEdges: Edge[] = edgeDescriptors.map((edge) => {
const isSubtask = edge.kind === 'subtask';
const label = isSubtask ? 'SUBTASK' : 'BLOCKS';
const isTransitive = transitiveEdges.has(`${edge.source}:blocks:${edge.target}`);
const edgeId = `${dep.target}:blocks:${issue.id}`;
const linkedToSelection = selectedId ? issue.id === selectedId || dep.target === selectedId : false;
const sourceIssue = beads.find((i) => i.id === dep.target);
const isInProgressEdge = issue.status === 'in_progress' || sourceIssue?.status === 'in_progress';
let stroke = '#64748b'; // default slate for subtasks / generic
let strokeBg = 'rgba(100, 116, 139, 0.3)';
let dashArray: string | undefined = undefined;
let opacity = 0.78;
graphEdges.push({
id: edgeId,
source: dep.target,
target: issue.id,
className: linkedToSelection ? 'workflow-edge-selected' : 'workflow-edge-muted',
animated: linkedToSelection || isInProgressEdge,
label: 'BLOCKS',
labelStyle: {
fill: linkedToSelection ? '#e2e8f0' : '#cbd5e1',
fontSize: 10,
fontWeight: 700,
letterSpacing: '0.08em',
},
labelBgPadding: [6, 3],
labelBgBorderRadius: 999,
labelBgStyle: {
fill: 'rgba(2, 6, 23, 0.92)',
stroke: linkedToSelection ? 'rgba(125, 211, 252, 0.35)' : 'rgba(251, 191, 36, 0.25)',
strokeWidth: 1,
},
style: {
stroke: linkedToSelection ? '#7dd3fc' : '#fbbf24',
strokeWidth: linkedToSelection ? 2.8 : 2.1,
opacity: linkedToSelection ? 1 : 0.78,
},
markerEnd: {
type: MarkerType.ArrowClosed,
color: linkedToSelection ? '#7dd3fc' : '#fbbf24',
width: 14,
height: 14,
},
});
const isFocusedPath = edge.isUpstreamOfFocus || edge.isDownstreamOfFocus || edge.isDirectlyFocused;
const isAnimated = isFocusedPath || edge.sourceStatus === 'in_progress';
if (isSubtask) {
stroke = isFocusedPath ? '#94a3b8' : '#64748b';
strokeBg = isFocusedPath ? 'rgba(148, 163, 184, 0.4)' : 'rgba(100, 116, 139, 0.3)';
dashArray = '6 4';
opacity = isFocusedPath ? 1 : (edge.isUnrelated ? 0.15 : 0.58);
} else {
// Evaluate Base Status
if (edge.sourceStatus === 'in_progress') {
stroke = '#fbbf24'; // Bright Amber
strokeBg = 'rgba(251, 191, 36, 0.25)';
} else if (edge.sourceStatus === 'blocked') {
stroke = '#f43f5e'; // Rose/Red for deep block
strokeBg = 'rgba(244, 63, 94, 0.25)';
} else {
stroke = '#3b82f6'; // Blue
strokeBg = 'rgba(59, 130, 246, 0.25)';
}
// Overrides for Selection
if (selectedId) {
if (edge.isUnrelated) {
stroke = '#1e293b'; // Super dim
strokeBg = 'transparent';
opacity = 0.15;
} else if (edge.isUpstreamOfFocus || (edge.isDirectlyFocused && edge.target === selectedId)) {
stroke = '#f59e0b'; // Amber for upstream blockers
strokeBg = 'rgba(245, 158, 11, 0.35)';
opacity = 1;
} else if (edge.isDownstreamOfFocus || (edge.isDirectlyFocused && edge.source === selectedId)) {
stroke = '#0ea5e9'; // Cyan for downstream impact
strokeBg = 'rgba(14, 165, 233, 0.35)';
opacity = 1;
}
}
// Transitive styling
if (isTransitive) {
dashArray = '4 4';
if (!selectedId || edge.isUnrelated) {
stroke = '#334155';
strokeBg = 'rgba(51, 65, 85, 0.3)';
opacity = 0.4;
} else {
opacity = 0.6; // Keep the focused color but make it dashed & slightly transparent
}
}
}
return {
id: edge.id,
source: edge.source,
target: edge.target,
className: isFocusedPath ? 'workflow-edge-selected' : 'workflow-edge-muted',
animated: isAnimated,
label,
labelStyle: {
fill: isFocusedPath ? '#e2e8f0' : '#cbd5e1',
fontSize: 10,
fontWeight: 700,
letterSpacing: '0.08em',
},
labelBgPadding: [6, 3],
labelBgBorderRadius: 999,
labelBgStyle: {
fill: 'rgba(2, 6, 23, 0.92)',
stroke: strokeBg,
strokeWidth: 1,
},
style: {
stroke,
strokeWidth: isFocusedPath ? 2.8 : 2.1,
opacity,
strokeDasharray: dashArray,
},
markerEnd: {
type: MarkerType.ArrowClosed,
color: stroke,
width: 14,
height: 14,
},
};
});
// --- Apply Offsets to Edge Data ---
const edgeGroups = new Map<string, Edge[]>();
for (const edge of graphEdges) {
const key = [edge.source, edge.target].sort().join('-');
if (!edgeGroups.has(key)) edgeGroups.set(key, []);
edgeGroups.get(key)!.push(edge);
}
for (const [unused_, groupEdges] of edgeGroups) {
if (groupEdges.length <= 1) continue;
// In Vertical layout, we might want X offset, in Horizontal Y offset.
// OffsetEdge component already handles adjusting the correct axis based on sourcePosition.
const step = 8;
const totalSpread = (groupEdges.length - 1) * step;
let currentOffset = -(totalSpread / 2);
for (const edge of groupEdges) {
edge.data = { ...edge.data, offset: currentOffset };
currentOffset += step;
}
}
@ -208,7 +291,7 @@ function WorkflowGraphInner({
nodes: layoutDagre(baseNodes, graphEdges, layoutDirection, layoutDensity),
edges: graphEdges,
};
}, [beads, hideClosed, selectedId, signalById, actionableNodeIds, cycleNodeIdSet, chainNodeIds, blockerTooltipMap, archetypes, assignMode, onSelect, onViewInSocial, onAssignMode, onViewTelemetry, layoutDirection, layoutDensity]);
}, [transitiveEdges, beads, hideClosed, selectedId, signalById, actionableNodeIds, cycleNodeIdSet, chainNodeIds, blockerTooltipMap, archetypes, assignMode, onSelect, onViewInSocial, onAssignMode, onViewTelemetry, layoutDirection, layoutDensity, showHierarchy]);
const nodeTypes: NodeTypes = useMemo(
() => ({
@ -217,9 +300,16 @@ function WorkflowGraphInner({
[],
);
const edgeTypes = useMemo(
() => ({
offset: OffsetEdge,
}),
[]
);
const defaultEdgeOptions = useMemo(
() => ({
type: 'smoothstep' as const,
type: 'offset' as const,
zIndex: 40,
interactionWidth: 24,
}),
@ -269,25 +359,34 @@ function WorkflowGraphInner({
</div>
<div className="absolute right-3 top-3 z-10 flex flex-wrap items-center gap-2">
<div className="inline-flex items-center gap-1 rounded-xl border border-[var(--border-subtle)] bg-[var(--surface-tertiary)] p-1">
<button
type="button"
onClick={() => setShowHierarchy((current) => !current)}
className={`rounded-md px-2 py-1 text-[11px] font-semibold transition-colors ${showHierarchy
? 'bg-[var(--surface-hover)] text-[var(--text-primary)]'
: 'text-[var(--text-secondary)] hover:text-[var(--text-primary)]'
}`}
title="Show parent/subtask links"
>
Hierarchy
</button>
<button
type="button"
onClick={() => setLayoutDirection('LR')}
className={`rounded-md px-2 py-1 text-[11px] font-semibold transition-colors ${
layoutDirection === 'LR'
? 'bg-[var(--surface-hover)] text-[var(--text-primary)]'
: 'text-[var(--text-secondary)] hover:text-[var(--text-primary)]'
}`}
className={`rounded-md px-2 py-1 text-[11px] font-semibold transition-colors ${layoutDirection === 'LR'
? 'bg-[var(--surface-hover)] text-[var(--text-primary)]'
: 'text-[var(--text-secondary)] hover:text-[var(--text-primary)]'
}`}
>
Horizontal
</button>
<button
type="button"
onClick={() => setLayoutDirection('TB')}
className={`rounded-md px-2 py-1 text-[11px] font-semibold transition-colors ${
layoutDirection === 'TB'
? 'bg-[var(--surface-hover)] text-[var(--text-primary)]'
: 'text-[var(--text-secondary)] hover:text-[var(--text-primary)]'
}`}
className={`rounded-md px-2 py-1 text-[11px] font-semibold transition-colors ${layoutDirection === 'TB'
? 'bg-[var(--surface-hover)] text-[var(--text-primary)]'
: 'text-[var(--text-secondary)] hover:text-[var(--text-primary)]'
}`}
>
Vertical
</button>
@ -296,22 +395,20 @@ function WorkflowGraphInner({
<button
type="button"
onClick={() => setLayoutDensity('compact')}
className={`rounded-md px-2 py-1 text-[11px] font-semibold transition-colors ${
layoutDensity === 'compact'
? 'bg-[var(--surface-hover)] text-[var(--text-primary)]'
: 'text-[var(--text-secondary)] hover:text-[var(--text-primary)]'
}`}
className={`rounded-md px-2 py-1 text-[11px] font-semibold transition-colors ${layoutDensity === 'compact'
? 'bg-[var(--surface-hover)] text-[var(--text-primary)]'
: 'text-[var(--text-secondary)] hover:text-[var(--text-primary)]'
}`}
>
Compact
</button>
<button
type="button"
onClick={() => setLayoutDensity('normal')}
className={`rounded-md px-2 py-1 text-[11px] font-semibold transition-colors ${
layoutDensity === 'normal'
? 'bg-[var(--surface-hover)] text-[var(--text-primary)]'
: 'text-[var(--text-secondary)] hover:text-[var(--text-primary)]'
}`}
className={`rounded-md px-2 py-1 text-[11px] font-semibold transition-colors ${layoutDensity === 'normal'
? 'bg-[var(--surface-hover)] text-[var(--text-primary)]'
: 'text-[var(--text-secondary)] hover:text-[var(--text-primary)]'
}`}
>
Normal
</button>
@ -339,6 +436,7 @@ function WorkflowGraphInner({
nodes={flowModel.nodes}
edges={flowModel.edges}
nodeTypes={nodeTypes}
edgeTypes={edgeTypes}
nodesDraggable={false}
nodesConnectable={false}
elementsSelectable
@ -348,7 +446,7 @@ function WorkflowGraphInner({
<Background gap={32} size={1} color="rgba(255,255,255,0.03)" />
</ReactFlow>
</div>
);
);
}
export function WorkflowGraph(props: WorkflowGraphProps) {

View file

@ -15,6 +15,8 @@ import dagre from 'dagre';
import type { BeadIssue } from '../../lib/types';
import type { AgentArchetype } from '../../lib/types-swarm';
import { buildGraphModel } from '../../lib/graph';
import { identifyTransitiveEdges } from '../../lib/graph-view';
// Custom Node for the Agent DAG
interface AgentNodeData extends Record<string, unknown> {
@ -152,19 +154,45 @@ function SpecializedAgentDagInner({ beads, archetypes, selectedId, onSelect }: {
const graphEdges: Edge[] = [];
const beadIds = new Set(visibleBeads.map(b => b.id));
const graphModel = buildGraphModel(visibleBeads);
const transitiveEdges = identifyTransitiveEdges(graphModel);
visibleBeads.forEach(issue => {
issue.dependencies.forEach(dep => {
if (dep.type === 'blocks' && beadIds.has(dep.target)) {
// issue depends on dep.target (issue is blocked by dep.target)
// Edge should flow from blocker to blocked
const sourceNode = visibleBeads.find(i => i.id === dep.target);
const isActiveBlocker = sourceNode?.status === 'in_progress' || sourceNode?.status === 'blocked';
const edgeId = `${dep.target}:blocks:${issue.id}`;
const isTransitive = transitiveEdges.has(edgeId);
const isLinked = issue.id === selectedId || dep.target === selectedId;
let stroke = '#475569';
let dashArray: string | undefined = undefined;
let opacity = 0.8;
if (isTransitive && !isLinked) {
stroke = '#334155';
dashArray = '4 4';
opacity = 0.4;
} else if (isActiveBlocker) {
stroke = isLinked ? '#7dd3fc' : '#fbbf24';
opacity = 1;
} else {
stroke = isLinked ? '#7dd3fc' : '#475569';
opacity = isLinked ? 1 : 0.8;
}
graphEdges.push({
id: `e-${dep.target}-${issue.id}`,
source: dep.target,
target: issue.id,
type: 'smoothstep',
animated: issue.status === 'in_progress' || issue.status === 'closed',
style: { stroke: '#475569', strokeWidth: 2 },
markerEnd: { type: MarkerType.ArrowClosed, color: '#475569' }
animated: isLinked || isActiveBlocker,
style: { stroke, strokeWidth: isLinked ? 2.8 : 2, opacity, strokeDasharray: dashArray },
markerEnd: { type: MarkerType.ArrowClosed, color: stroke }
});
}
});

View file

@ -65,6 +65,10 @@ export function useBeadsSubscription(
}
}, [projectRoot, onUpdate]);
useEffect(() => {
void refresh({ silent: true });
}, [refresh]);
useEffect(() => {
const source = new EventSource(`/api/events?projectRoot=${encodeURIComponent(projectRoot)}`);

200
src/lib/epic-graph.ts Normal file
View file

@ -0,0 +1,200 @@
import type { BeadIssue } from './types';
export interface WorkflowEdgeDescriptor {
id: string;
source: string;
target: string;
kind: 'blocks' | 'subtask';
isUpstreamOfFocus: boolean;
isDownstreamOfFocus: boolean;
isDirectlyFocused: boolean;
isUnrelated: boolean;
sourceStatus: string;
targetStatus: string;
}
interface BuildWorkflowEdgesOptions {
issues: BeadIssue[];
visibleIds: Set<string>;
selectedId: string | null;
includeHierarchy: boolean;
}
export function collectEpicDescendantIds(issues: BeadIssue[], epicId: string): Set<string> {
const childrenByParent = new Map<string, string[]>();
const issueById = new Map(issues.map((issue) => [issue.id, issue]));
for (const issue of issues) {
for (const dep of issue.dependencies) {
if (dep.type !== 'parent') continue;
const list = childrenByParent.get(dep.target) ?? [];
list.push(issue.id);
childrenByParent.set(dep.target, list);
}
}
const descendants = new Set<string>();
const queue = [...(childrenByParent.get(epicId) ?? [])];
const seen = new Set<string>([epicId]);
while (queue.length > 0) {
const id = queue.shift();
if (!id || seen.has(id)) continue;
seen.add(id);
const issue = issueById.get(id);
if (issue && issue.issue_type !== 'epic') {
descendants.add(id);
}
const children = childrenByParent.get(id) ?? [];
for (const child of children) {
if (!seen.has(child)) {
queue.push(child);
}
}
}
return descendants;
}
export function buildWorkflowEdges({
issues,
visibleIds,
selectedId,
includeHierarchy,
}: BuildWorkflowEdgesOptions): WorkflowEdgeDescriptor[] {
const issueById = new Map(issues.map((issue) => [issue.id, issue]));
const edgeIds = new Set<string>();
const edges: WorkflowEdgeDescriptor[] = [];
const upstreamIds = new Set<string>();
const downstreamIds = new Set<string>();
if (selectedId) {
upstreamIds.add(selectedId);
downstreamIds.add(selectedId);
// Build adjacency just for tracing blockers
const outgoing = new Map<string, string[]>(); // id -> nodes it blocks
const incoming = new Map<string, string[]>(); // id -> nodes blocking it
for (const issue of issues) {
for (const dep of issue.dependencies) {
if (dep.type === 'blocks') {
const blocker = dep.target;
const blocked = issue.id;
if (!outgoing.has(blocker)) outgoing.set(blocker, []);
if (!incoming.has(blocked)) incoming.set(blocked, []);
outgoing.get(blocker)!.push(blocked);
incoming.get(blocked)!.push(blocker);
}
}
}
// Trace incoming (upstream blockers)
let queue = [selectedId];
while (queue.length > 0) {
const curr = queue.shift()!;
for (const b of (incoming.get(curr) || [])) {
if (!upstreamIds.has(b)) {
upstreamIds.add(b);
queue.push(b);
}
}
}
// Trace outgoing (downstream blocked)
queue = [selectedId];
while (queue.length > 0) {
const curr = queue.shift()!;
for (const b of (outgoing.get(curr) || [])) {
if (!downstreamIds.has(b)) {
downstreamIds.add(b);
queue.push(b);
}
}
}
}
for (const issue of issues) {
if (!visibleIds.has(issue.id)) continue;
for (const dep of issue.dependencies) {
if (!visibleIds.has(dep.target)) continue;
if (dep.target === issue.id) continue;
if (dep.type === 'blocks') {
const source = dep.target;
const target = issue.id;
const id = `${source}:blocks:${target}`;
if (edgeIds.has(id)) continue;
edgeIds.add(id);
const sourceIssue = issueById.get(source);
const sourceStatus = sourceIssue?.status || 'open';
const targetStatus = issue.status;
const isUpstreamOfFocus = selectedId ? upstreamIds.has(source) && upstreamIds.has(target) : false;
const isDownstreamOfFocus = selectedId ? downstreamIds.has(source) && downstreamIds.has(target) : false;
const isDirectlyFocused = selectedId ? source === selectedId || target === selectedId : false;
let isUnrelated = false;
if (selectedId) {
isUnrelated = !isUpstreamOfFocus && !isDownstreamOfFocus && !isDirectlyFocused;
}
edges.push({
id,
source,
target,
kind: 'blocks',
isUpstreamOfFocus,
isDownstreamOfFocus,
isDirectlyFocused,
isUnrelated,
sourceStatus,
targetStatus
});
}
if (includeHierarchy && dep.type === 'parent') {
const source = dep.target;
const target = issue.id;
const id = `${source}:subtask:${target}`;
if (edgeIds.has(id)) continue;
edgeIds.add(id);
const sourceIssue = issueById.get(source);
const sourceStatus = sourceIssue?.status || 'open';
const targetStatus = issue.status;
const isUpstreamOfFocus = selectedId ? upstreamIds.has(source) && upstreamIds.has(target) : false;
const isDownstreamOfFocus = selectedId ? downstreamIds.has(source) && downstreamIds.has(target) : false;
const isDirectlyFocused = selectedId ? source === selectedId || target === selectedId : false;
let isUnrelated = false;
if (selectedId) {
isUnrelated = !isUpstreamOfFocus && !isDownstreamOfFocus && !isDirectlyFocused;
}
edges.push({
id,
source,
target,
kind: 'subtask',
isUpstreamOfFocus,
isDownstreamOfFocus,
isDirectlyFocused,
isUnrelated,
sourceStatus,
targetStatus
});
}
}
}
return edges;
}

View file

@ -419,6 +419,60 @@ export function analyzeBlockedChain(model: GraphModel, options: { focusId: strin
};
}
/**
* Identifies edges in the graph that are transitive (redundant).
* For example, if A -> B -> C exists, the edge A -> C is transitive.
* This is primarily used to visually de-emphasize redundant blocks in the DAG.
* Returns a Set of edge keys formatted as `${source}:blocks:${target}`.
*/
export function identifyTransitiveEdges(model: GraphModel): Set<string> {
const transitiveEdgeKeys = new Set<string>();
for (const edge of model.edges) {
if (edge.type !== 'blocks') continue;
const sourceId = edge.source;
const targetId = edge.target;
const visited = new Set<string>();
const queue = [sourceId];
visited.add(sourceId);
let foundAlternativePath = false;
while (queue.length > 0) {
const currentId = queue.shift()!;
if (currentId === targetId && currentId !== sourceId) {
foundAlternativePath = true;
break;
}
const adjacency = model.adjacency[currentId];
if (!adjacency) continue;
for (const outEdge of adjacency.outgoing) {
if (outEdge.type !== 'blocks') continue;
// Skip the immediate direct edge we are evaluating
if (currentId === sourceId && outEdge.target === targetId) {
continue;
}
if (!visited.has(outEdge.target)) {
visited.add(outEdge.target);
queue.push(outEdge.target);
}
}
}
if (foundAlternativePath) {
transitiveEdgeKeys.add(`${sourceId}:blocks:${targetId}`);
}
}
return transitiveEdgeKeys;
}
export interface CycleAnomaly {
cycles: string[][];
cycleNodeIds: string[];

103
src/lib/install-manifest.ts Normal file
View file

@ -0,0 +1,103 @@
export const INSTALLER_SCHEMA_VERSION = 'installer.v1' as const;
export const DRIVER_REMEDIATION_MODE = 'detect_only' as const;
export interface InstallerManifest {
version: typeof INSTALLER_SCHEMA_VERSION;
distribution: {
packageName: string;
shimNames: string[];
};
wrappers: {
windows: {
script: string;
};
posix: {
script: string;
};
};
runtime: {
start: string;
open: string;
status: string;
};
driver: {
remediationMode: typeof DRIVER_REMEDIATION_MODE;
installSideEffects: false;
};
}
export type InstallerManifestValidationResult =
| { ok: true; value: InstallerManifest }
| { ok: false; error: string };
function fail(error: string): InstallerManifestValidationResult {
return { ok: false, error };
}
function isObject(input: unknown): input is Record<string, unknown> {
return typeof input === 'object' && input !== null;
}
function nonEmptyString(input: unknown): input is string {
return typeof input === 'string' && input.trim().length > 0;
}
export function validateInstallerManifest(input: unknown): InstallerManifestValidationResult {
if (!isObject(input)) return fail('manifest must be an object');
if (input.version !== INSTALLER_SCHEMA_VERSION) {
return fail(`version must be "${INSTALLER_SCHEMA_VERSION}"`);
}
if (!isObject(input.distribution)) return fail('distribution is required');
if (!nonEmptyString(input.distribution.packageName)) return fail('distribution.packageName is required');
if (!Array.isArray(input.distribution.shimNames) || input.distribution.shimNames.length === 0) {
return fail('distribution.shimNames must be a non-empty array');
}
for (const shimName of input.distribution.shimNames) {
if (!nonEmptyString(shimName)) return fail('distribution.shimNames must contain only non-empty strings');
}
if (!isObject(input.wrappers)) return fail('wrappers is required');
if (!isObject(input.wrappers.windows) || !nonEmptyString(input.wrappers.windows.script)) {
return fail('wrappers.windows.script is required');
}
if (!isObject(input.wrappers.posix) || !nonEmptyString(input.wrappers.posix.script)) {
return fail('wrappers.posix.script is required');
}
if (!isObject(input.runtime)) return fail('runtime is required');
if (!nonEmptyString(input.runtime.start)) return fail('runtime.start is required');
if (!nonEmptyString(input.runtime.open)) return fail('runtime.open is required');
if (!nonEmptyString(input.runtime.status)) return fail('runtime.status is required');
if (!isObject(input.driver)) return fail('driver is required');
if (input.driver.remediationMode !== DRIVER_REMEDIATION_MODE) {
return fail(`driver.remediationMode must be "${DRIVER_REMEDIATION_MODE}"`);
}
if (input.driver.installSideEffects !== false) {
return fail('driver.installSideEffects must be false');
}
return { ok: true, value: input as unknown as InstallerManifest };
}
export const canonicalInstallerManifest: InstallerManifest = {
version: INSTALLER_SCHEMA_VERSION,
distribution: {
packageName: 'beadboard',
shimNames: ['bb', 'beadboard'],
},
wrappers: {
windows: { script: 'install.ps1' },
posix: { script: 'install.sh' },
},
runtime: {
start: 'beadboard start',
open: 'beadboard open',
status: 'beadboard status',
},
driver: {
remediationMode: DRIVER_REMEDIATION_MODE,
installSideEffects: false,
},
};

View file

@ -0,0 +1,12 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
import path from 'node:path';
test('dependency graph uses hide-closed filtered epics for epic chip strip', async () => {
const file = await fs.readFile(path.join(process.cwd(), 'src/components/graph/dependency-graph-page.tsx'), 'utf8');
assert.ok(file.includes('const selectableEpics = useMemo'), 'expected selectableEpics memoized list');
assert.ok(file.includes('epics={selectableEpics}'), 'expected EpicChipStrip to receive selectableEpics');
assert.ok(file.includes('selectableEpics.some((epic) => epic.id === requestedEpicId)'), 'expected requested epic validation against selectable epics');
});

View file

@ -0,0 +1,84 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { shouldHideEpicEntry, type LeftPanelFilters } from '../../../src/components/shared/left-panel';
const defaultFilters: LeftPanelFilters = {
query: '',
status: 'all',
priority: 'all',
preset: 'all',
hideClosed: true,
};
test('does not hide epics with no children when hideClosed is the only active toggle', () => {
const hidden = shouldHideEpicEntry({
epicStatus: 'open',
matchedChildrenCount: 0,
totalChildrenCount: 0,
isSelected: false,
filters: defaultFilters,
});
assert.equal(hidden, false);
});
test('hides epics with only closed children when hideClosed is enabled', () => {
const hidden = shouldHideEpicEntry({
epicStatus: 'open',
matchedChildrenCount: 0,
totalChildrenCount: 4,
isSelected: false,
filters: defaultFilters,
});
assert.equal(hidden, true);
});
test('hides epic with children when query filter excludes all children', () => {
const hidden = shouldHideEpicEntry({
epicStatus: 'open',
matchedChildrenCount: 0,
totalChildrenCount: 3,
isSelected: false,
filters: { ...defaultFilters, query: 'nonexistent' },
});
assert.equal(hidden, true);
});
test('keeps selected epic visible even when no children match filters', () => {
const hidden = shouldHideEpicEntry({
epicStatus: 'open',
matchedChildrenCount: 0,
totalChildrenCount: 5,
isSelected: true,
filters: { ...defaultFilters, status: 'blocked' },
});
assert.equal(hidden, false);
});
test('hides closed epic even when it has no children', () => {
const hidden = shouldHideEpicEntry({
epicStatus: 'closed',
matchedChildrenCount: 0,
totalChildrenCount: 0,
isSelected: false,
filters: defaultFilters,
});
assert.equal(hidden, true);
});
test('hides closed selected epic when hideClosed is enabled', () => {
const hidden = shouldHideEpicEntry({
epicStatus: 'closed',
matchedChildrenCount: 2,
totalChildrenCount: 2,
isSelected: true,
filters: defaultFilters,
});
assert.equal(hidden, true);
});

View file

@ -0,0 +1,11 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
import path from 'node:path';
test('UnifiedShell clears selected closed epic when hideClosed is enabled', async () => {
const file = await fs.readFile(path.join(process.cwd(), 'src/components/shared/unified-shell.tsx'), 'utf8');
assert.ok(file.includes('if (epic.status === \'closed\' || epic.status === \'tombstone\')'), 'expected closed epic guard');
assert.ok(file.includes('setEpicId(null);'), 'expected selected epic reset');
});

View file

@ -49,8 +49,8 @@ test('UnifiedShell - checks bd health and renders setup warning', async () => {
// Test that AssignmentPanel is rendered conditionally based on view and assignMode
test('UnifiedShell - renders AssignmentPanel conditionally', async () => {
const fileContent = await fs.readFile(path.join(process.cwd(), 'src/components/shared/unified-shell.tsx'), 'utf-8');
// Check for the condition: view === 'graph' && assignMode
assert.ok(fileContent.includes("view === 'graph' && assignMode"), 'Should check view === graph && assignMode condition for AssignmentPanel');
// Check for the condition: assignMode && !taskId
assert.ok(fileContent.includes("assignMode && !taskId"), 'Should check assignMode && !taskId condition for AssignmentPanel');
});
// Test that SwarmWorkspace import is removed (deprecated)

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