diff --git a/.agent/skills/heroui-react b/.agent/skills/heroui-react new file mode 120000 index 0000000..413da29 --- /dev/null +++ b/.agent/skills/heroui-react @@ -0,0 +1 @@ +../../.agents/skills/heroui-react \ No newline at end of file diff --git a/.agents/skills/heroui-react/LICENSE.txt b/.agents/skills/heroui-react/LICENSE.txt new file mode 100644 index 0000000..763029c --- /dev/null +++ b/.agents/skills/heroui-react/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 NextUI Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/.agents/skills/heroui-react/SKILL.md b/.agents/skills/heroui-react/SKILL.md new file mode 100644 index 0000000..1739912 --- /dev/null +++ b/.agents/skills/heroui-react/SKILL.md @@ -0,0 +1,253 @@ +--- +name: heroui-react +description: "HeroUI v3 React component library (Tailwind CSS v4 + React Aria). Use when working with HeroUI components, installing HeroUI, customizing HeroUI themes, or accessing HeroUI component documentation. Keywords: HeroUI, Hero UI, heroui, @heroui/react, @heroui/styles." +metadata: + author: heroui + version: "2.0.0" +--- + +# HeroUI v3 React Development Guide + +HeroUI v3 is a component library built on **Tailwind CSS v4** and **React Aria Components**, providing accessible, customizable UI components for React applications. + +--- + +## CRITICAL: v3 Only - Ignore v2 Knowledge + +**This guide is for HeroUI v3 ONLY.** Do NOT use any prior knowledge of HeroUI v2. + +### What Changed in v3 + +| Feature | v2 (DO NOT USE) | v3 (USE THIS) | +| ------------- | --------------------------------- | ------------------------------------------- | +| Provider | `` required | **No Provider needed** | +| Animations | `framer-motion` package | CSS-based, no extra deps | +| Component API | Flat props: `` | Compound: `` | +| Styling | Tailwind v3 + `@heroui/theme` | Tailwind v4 + `@heroui/styles@beta` | +| Packages | `@heroui/system`, `@heroui/theme` | `@heroui/react@beta`, `@heroui/styles@beta` | + +### WRONG (v2 patterns) + +```tsx +// DO NOT DO THIS - v2 pattern +import { HeroUIProvider } from "@heroui/react"; +import { motion } from "framer-motion"; + + + +; +``` + +### CORRECT (v3 patterns) + +```tsx +// DO THIS - v3 pattern (no provider, compound components) +import { Card } from "@heroui/react@beta"; + + + + Product + A great product + +; +``` + +**Always fetch v3 docs before implementing.** Do not assume v2 patterns work. + +--- + +## Core Principles + +- Semantic variants (`primary`, `secondary`, `tertiary`) over visual descriptions +- Composition over configuration (compound components) +- CSS variable-based theming with `oklch` color space +- BEM naming convention for predictable styling + +--- + +## Accessing Documentation & Component Information + +**For component details, examples, props, and implementation patterns, always fetch documentation:** + +### Using Scripts + +```bash +# List all available components +node scripts/list_components.mjs + +# Get component documentation (MDX) +node scripts/get_component_docs.mjs Button +node scripts/get_component_docs.mjs Button Card TextField + +# Get component source code +node scripts/get_source.mjs Button + +# Get component CSS styles (BEM classes) +node scripts/get_styles.mjs Button + +# Get theme variables +node scripts/get_theme.mjs + +# Get non-component docs (guides, releases) +node scripts/get_docs.mjs /docs/react/getting-started/theming +``` + +### Direct MDX URLs + +Component docs: `https://v3.heroui.com/docs/react/components/{component-name}.mdx` + +Examples: + +- Button: `https://v3.heroui.com/docs/react/components/button.mdx` +- Modal: `https://v3.heroui.com/docs/react/components/modal.mdx` +- Form: `https://v3.heroui.com/docs/react/components/form.mdx` + +Getting started guides: `https://v3.heroui.com/docs/react/getting-started/{topic}.mdx` + +**Important:** Always fetch component docs before implementing. The MDX docs include complete examples, props, anatomy, and API references. + +--- + +## Installation Essentials + +**CRITICAL**: HeroUI v3 is currently in BETA. Always use `@beta` tag when installing packages. + +### Quick Install + +```bash +npm i @heroui/styles@beta @heroui/react@beta tailwind-variants +``` + +### Framework Setup (Next.js App Router - Recommended) + +1. **Install dependencies:** + +```bash +npm i @heroui/styles@beta @heroui/react@beta tailwind-variants tailwindcss @tailwindcss/postcss postcss +``` + +2. **Create/update `app/globals.css`:** + +```css +/* Tailwind CSS v4 - Must be first */ +@import "tailwindcss"; + +/* HeroUI v3 styles - Must be after Tailwind */ +@import "@heroui/styles"; +``` + +3. **Import in `app/layout.tsx`:** + +```tsx +import "./globals.css"; + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + + {/* No Provider needed in HeroUI v3! */} + {children} + + + ); +} +``` + +4. **Configure PostCSS (`postcss.config.mjs`):** + +```js +export default { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; +``` + +### Critical Setup Requirements + +1. **Tailwind CSS v4 is MANDATORY** - HeroUI v3 will NOT work with Tailwind CSS v3 +2. **No Provider Required** - Unlike HeroUI v2, v3 components work directly without a Provider +3. **Use Compound Components** - Components use compound structure (e.g., `Card.Header`, `Card.Content`) +4. **Use onPress, not onClick** - For better accessibility, use `onPress` event handlers +5. **Import Order Matters** - Always import Tailwind CSS before HeroUI styles + +--- + +## Component Patterns + +HeroUI v3 uses **compound component patterns**. Each component has subcomponents accessed via dot notation. + +**Example - Card:** + +```tsx + + + Title + Description + + {/* Content */} + {/* Actions */} + +``` + +**Key Points:** + +- Always use compound structure - don't flatten to props +- Subcomponents are accessed via dot notation (e.g., `Card.Header`) +- Each subcomponent may have its own props +- **Fetch component docs for complete anatomy and examples** + +--- + +## Semantic Variants + +HeroUI uses semantic naming to communicate functional intent: + +| Variant | Purpose | Usage | +| ----------- | --------------------------------- | -------------- | +| `primary` | Main action to move forward | 1 per context | +| `secondary` | Alternative actions | Multiple | +| `tertiary` | Dismissive actions (cancel, skip) | Sparingly | +| `danger` | Destructive actions | When needed | +| `ghost` | Low-emphasis actions | Minimal weight | +| `outline` | Secondary actions | Bordered style | + +**Don't use raw colors** - semantic variants adapt to themes and accessibility. + +--- + +## Theming + +HeroUI v3 uses CSS variables with `oklch` color space: + +```css +:root { + --accent: oklch(0.6204 0.195 253.83); + --accent-foreground: var(--snow); + --background: oklch(0.9702 0 0); + --foreground: var(--eclipse); +} +``` + +**Get current theme variables:** + +```bash +node scripts/get_theme.mjs +``` + +**Color naming:** + +- Without suffix = background (e.g., `--accent`) +- With `-foreground` = text color (e.g., `--accent-foreground`) + +**Theme switching:** + +```html + +``` + +For detailed theming, fetch: `https://v3.heroui.com/docs/react/getting-started/theming.mdx` diff --git a/.agents/skills/heroui-react/scripts/get_component_docs.mjs b/.agents/skills/heroui-react/scripts/get_component_docs.mjs new file mode 100644 index 0000000..5477a3d --- /dev/null +++ b/.agents/skills/heroui-react/scripts/get_component_docs.mjs @@ -0,0 +1,157 @@ +#!/usr/bin/env node +/** + * Get complete component documentation (MDX) for HeroUI v3 components. + * + * Usage: + * node get_component_docs.mjs Button + * node get_component_docs.mjs Button Card TextField + * + * Output: + * MDX documentation including imports, usage, variants, props, examples + */ + +const API_BASE = process.env.HEROUI_API_BASE || "https://mcp-api.heroui.com"; +const FALLBACK_BASE = "https://v3.heroui.com"; +const APP_PARAM = "app=react-skills"; + +/** + * Convert PascalCase to kebab-case. + */ +function toKebabCase(name) { + return name + .replace(/([a-z])([A-Z])/g, "$1-$2") + .replace(/([A-Z])([A-Z][a-z])/g, "$1-$2") + .toLowerCase(); +} + +/** + * Fetch data from HeroUI API with app parameter for analytics. + */ +async function fetchApi(endpoint, method = "GET", body = null) { + const separator = endpoint.includes("?") ? "&" : "?"; + const url = `${API_BASE}${endpoint}${separator}${APP_PARAM}`; + + try { + const options = { + headers: { + "Content-Type": "application/json", + "User-Agent": "HeroUI-Skill/1.0", + }, + method, + signal: AbortSignal.timeout(30000), + }; + + if (body) { + options.body = JSON.stringify(body); + } + + const response = await fetch(url, options); + + if (!response.ok) { + return null; + } + + return await response.json(); + } catch { + return null; + } +} + +/** + * Fetch MDX directly from v3.heroui.com as fallback. + */ +async function fetchFallback(component) { + const kebabName = toKebabCase(component); + const url = `${FALLBACK_BASE}/docs/react/components/${kebabName}.mdx`; + + try { + const response = await fetch(url, { + headers: {"User-Agent": "HeroUI-Skill/1.0"}, + signal: AbortSignal.timeout(30000), + }); + + if (!response.ok) { + return {component, error: `Failed to fetch docs for ${component}`}; + } + + const content = await response.text(); + + return { + component, + content, + contentType: "mdx", + source: "fallback", + url, + }; + } catch { + return {component, error: `Failed to fetch docs for ${component}`}; + } +} + +/** + * Main function to get component documentation. + */ +async function main() { + const args = process.argv.slice(2); + + if (args.length === 0) { + console.error("Usage: node get_component_docs.mjs [Component2] ..."); + console.error("Example: node get_component_docs.mjs Button Card"); + process.exit(1); + } + + const components = args; + + // Try API first - use POST /v1/components/docs for batch requests + console.error(`# Fetching docs for: ${components.join(", ")}...`); + const data = await fetchApi("/v1/components/docs", "POST", {components}); + + if (data && data.results) { + // Output results + if (data.results.length === 1) { + // Single component - output content directly for easier reading + const result = data.results[0]; + + if (result.content) { + console.log(result.content); + } else if (result.error) { + console.error(`# Error for ${result.component}: ${result.error}`); + console.log(JSON.stringify(result, null, 2)); + } else { + console.log(JSON.stringify(result, null, 2)); + } + } else { + // Multiple components - output as JSON array + console.log(JSON.stringify(data, null, 2)); + } + + return; + } + + // Fallback to individual component fetches + console.error("# API failed, using fallback..."); + const results = []; + + for (const component of components) { + const result = await fetchFallback(component); + + results.push(result); + } + + // Output results + if (results.length === 1) { + // Single component - output content directly for easier reading + const result = results[0]; + + if (result.content) { + console.log(result.content); + } else { + console.log(JSON.stringify(result, null, 2)); + } + } else { + // Multiple components - output as JSON array + console.log(JSON.stringify(results, null, 2)); + } +} + +main(); diff --git a/.agents/skills/heroui-react/scripts/get_docs.mjs b/.agents/skills/heroui-react/scripts/get_docs.mjs new file mode 100644 index 0000000..85da626 --- /dev/null +++ b/.agents/skills/heroui-react/scripts/get_docs.mjs @@ -0,0 +1,148 @@ +#!/usr/bin/env node +/** + * Get non-component HeroUI documentation (guides, theming, releases). + * + * Usage: + * node get_docs.mjs /docs/react/getting-started/theming + * node get_docs.mjs /docs/react/releases/v3-0-0-beta-3 + * + * Output: + * MDX documentation content + * + * Note: For component docs, use get_component_docs.mjs instead. + */ + +const API_BASE = process.env.HEROUI_API_BASE || "https://mcp-api.heroui.com"; +const FALLBACK_BASE = "https://v3.heroui.com"; +const APP_PARAM = "app=react-skills"; + +/** + * Fetch documentation from HeroUI API. + * Uses v1 endpoint pattern: /v1/docs/:path + */ +async function fetchApi(path) { + // The v1 API expects path without /docs/ prefix + // Input: /docs/react/getting-started/theming + // API expects: react/getting-started/theming (route is /v1/docs/:path(*)) + let apiPath = path.startsWith("/docs/") + ? path.slice(6) // Remove /docs/ prefix + : path.startsWith("/") + ? path.slice(1) // Remove leading / + : path; + + const separator = "?"; + const url = `${API_BASE}/v1/docs/${apiPath}${separator}${APP_PARAM}`; + + try { + const response = await fetch(url, { + headers: {"User-Agent": "HeroUI-Skill/1.0"}, + signal: AbortSignal.timeout(30000), + }); + + if (!response.ok) { + console.error(`# API Error: HTTP ${response.status}`); + + return null; + } + + return await response.json(); + } catch (error) { + console.error(`# API Error: ${error.message}`); + + return null; + } +} + +/** + * Fetch MDX directly from v3.heroui.com as fallback. + */ +async function fetchFallback(path) { + // Ensure path starts with /docs and ends with .mdx + let cleanPath = path.replace(/^\//, ""); + + if (!cleanPath.endsWith(".mdx")) { + cleanPath = `${cleanPath}.mdx`; + } + + const url = `${FALLBACK_BASE}/${cleanPath}`; + + try { + const response = await fetch(url, { + headers: {"User-Agent": "HeroUI-Skill/1.0"}, + signal: AbortSignal.timeout(30000), + }); + + if (!response.ok) { + return {error: `HTTP ${response.status}: ${response.statusText}`, path}; + } + + const content = await response.text(); + + return { + content, + contentType: "mdx", + path, + source: "fallback", + url, + }; + } catch (error) { + return {error: `Fetch Error: ${error.message}`, path}; + } +} + +/** + * Main function to get documentation for specified path. + */ +async function main() { + const args = process.argv.slice(2); + + if (args.length === 0) { + console.error("Usage: node get_docs.mjs "); + console.error("Example: node get_docs.mjs /docs/react/getting-started/theming"); + console.error(); + console.error("Available paths include:"); + console.error(" /docs/react/getting-started/theming"); + console.error(" /docs/react/getting-started/colors"); + console.error(" /docs/react/getting-started/animations"); + console.error(" /docs/react/releases/v3-0-0-beta-3"); + console.error(); + console.error("Note: For component docs, use get_component_docs.mjs instead."); + process.exit(1); + } + + const path = args[0]; + + // Check if user is trying to get component docs + if (path.includes("/components/")) { + console.error("# Warning: Use get_component_docs.mjs for component documentation."); + const componentName = path.split("/").pop().replace(".mdx", ""); + const titleCase = componentName.charAt(0).toUpperCase() + componentName.slice(1); + + console.error(`# Example: node get_component_docs.mjs ${titleCase}`); + } + + console.error(`# Fetching documentation for ${path}...`); + + // Try API first + const data = await fetchApi(path); + + if (data && data.content) { + data.source = "api"; + console.log(data.content); + + return; + } + + // Fallback to direct fetch + console.error("# API failed, using fallback..."); + const fallbackData = await fetchFallback(path); + + if (fallbackData.content) { + console.log(fallbackData.content); + } else { + console.log(JSON.stringify(fallbackData, null, 2)); + process.exit(1); + } +} + +main(); diff --git a/.agents/skills/heroui-react/scripts/get_source.mjs b/.agents/skills/heroui-react/scripts/get_source.mjs new file mode 100644 index 0000000..9dc3355 --- /dev/null +++ b/.agents/skills/heroui-react/scripts/get_source.mjs @@ -0,0 +1,160 @@ +#!/usr/bin/env node +/** + * Get React/TypeScript source code implementation for HeroUI v3 components. + * + * Usage: + * node get_source.mjs Button + * node get_source.mjs Button Accordion Card + * + * Output: + * Full TSX source code with GitHub URL for each component + */ + +const API_BASE = process.env.HEROUI_API_BASE || "https://mcp-api.heroui.com"; +const GITHUB_RAW_BASE = "https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3"; +const APP_PARAM = "app=react-skills"; + +/** + * Fetch data from HeroUI API with app parameter for analytics. + */ +async function fetchApi(endpoint, method = "GET", body = null) { + const separator = endpoint.includes("?") ? "&" : "?"; + const url = `${API_BASE}${endpoint}${separator}${APP_PARAM}`; + + try { + const options = { + headers: { + "Content-Type": "application/json", + "User-Agent": "HeroUI-Skill/1.0", + }, + method, + signal: AbortSignal.timeout(30000), + }; + + if (body) { + options.body = JSON.stringify(body); + } + + const response = await fetch(url, options); + + if (!response.ok) { + console.error(`# API Error: HTTP ${response.status}`); + + return null; + } + + return await response.json(); + } catch (error) { + console.error(`# API Error: ${error.message}`); + + return null; + } +} + +/** + * Fetch source code directly from GitHub as fallback. + */ +async function fetchGithubFallback(component) { + // Try common patterns for component paths + const patterns = [ + `packages/react/src/components/${component.toLowerCase()}/${component.toLowerCase()}.tsx`, + `packages/react/src/components/${component.toLowerCase()}/index.tsx`, + ]; + + for (const path of patterns) { + const url = `${GITHUB_RAW_BASE}/${path}`; + + try { + const response = await fetch(url, { + headers: {"User-Agent": "HeroUI-Skill/1.0"}, + signal: AbortSignal.timeout(30000), + }); + + if (response.ok) { + const content = await response.text(); + + return { + component, + filePath: path, + githubUrl: `https://github.com/heroui-inc/heroui/blob/v3/${path}`, + source: "fallback", + sourceCode: content, + }; + } + } catch { + continue; + } + } + + return {component, error: `Failed to fetch source for ${component}`}; +} + +/** + * Main function to get source code for specified components. + */ +async function main() { + const args = process.argv.slice(2); + + if (args.length === 0) { + console.error("Usage: node get_source.mjs [Component2] ..."); + console.error("Example: node get_source.mjs Button Accordion"); + process.exit(1); + } + + const components = args; + + // Try API first + console.error(`# Fetching source code for: ${components.join(", ")}...`); + const data = await fetchApi("/v1/components/source", "POST", {components}); + + if (data && data.results) { + for (const result of data.results) { + result.source = "api"; + } + + // Output results + if (data.results.length === 1) { + const result = data.results[0]; + + if (result.sourceCode) { + console.log(`// File: ${result.filePath || "unknown"}`); + console.log(`// GitHub: ${result.githubUrl || "unknown"}`); + console.log(); + console.log(result.sourceCode); + } else { + console.log(JSON.stringify(result, null, 2)); + } + } else { + console.log(JSON.stringify(data, null, 2)); + } + + return; + } + + // Fallback to GitHub direct fetch + console.error("# API failed, using GitHub fallback..."); + const results = []; + + for (const component of components) { + const result = await fetchGithubFallback(component); + + results.push(result); + } + + if (results.length === 1) { + const result = results[0]; + + if (result.sourceCode) { + console.log(`// File: ${result.filePath || "unknown"}`); + console.log(`// GitHub: ${result.githubUrl || "unknown"}`); + console.log(); + console.log(result.sourceCode); + } else { + console.log(JSON.stringify(result, null, 2)); + } + } else { + console.log(JSON.stringify({results}, null, 2)); + } +} + +main(); diff --git a/.agents/skills/heroui-react/scripts/get_styles.mjs b/.agents/skills/heroui-react/scripts/get_styles.mjs new file mode 100644 index 0000000..5323806 --- /dev/null +++ b/.agents/skills/heroui-react/scripts/get_styles.mjs @@ -0,0 +1,160 @@ +#!/usr/bin/env node +/** + * Get CSS styles (BEM classes) for HeroUI v3 components. + * + * Usage: + * node get_styles.mjs Button + * node get_styles.mjs Button Card Chip + * + * Output: + * CSS file content with BEM classes and GitHub URL for each component + */ + +const API_BASE = process.env.HEROUI_API_BASE || "https://mcp-api.heroui.com"; +const GITHUB_RAW_BASE = "https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3"; +const APP_PARAM = "app=react-skills"; + +/** + * Fetch data from HeroUI API with app parameter for analytics. + */ +async function fetchApi(endpoint, method = "GET", body = null) { + const separator = endpoint.includes("?") ? "&" : "?"; + const url = `${API_BASE}${endpoint}${separator}${APP_PARAM}`; + + try { + const options = { + headers: { + "Content-Type": "application/json", + "User-Agent": "HeroUI-Skill/1.0", + }, + method, + signal: AbortSignal.timeout(30000), + }; + + if (body) { + options.body = JSON.stringify(body); + } + + const response = await fetch(url, options); + + if (!response.ok) { + console.error(`# API Error: HTTP ${response.status}`); + + return null; + } + + return await response.json(); + } catch (error) { + console.error(`# API Error: ${error.message}`); + + return null; + } +} + +/** + * Fetch CSS styles directly from GitHub as fallback. + */ +async function fetchGithubFallback(component) { + // Try common patterns for style paths + const patterns = [ + `packages/styles/src/components/${component.toLowerCase()}.css`, + `packages/styles/components/${component.toLowerCase()}.css`, + ]; + + for (const path of patterns) { + const url = `${GITHUB_RAW_BASE}/${path}`; + + try { + const response = await fetch(url, { + headers: {"User-Agent": "HeroUI-Skill/1.0"}, + signal: AbortSignal.timeout(30000), + }); + + if (response.ok) { + const content = await response.text(); + + return { + component, + filePath: path, + githubUrl: `https://github.com/heroui-inc/heroui/blob/v3/${path}`, + source: "fallback", + stylesCode: content, + }; + } + } catch { + continue; + } + } + + return {component, error: `Failed to fetch styles for ${component}`}; +} + +/** + * Main function to get CSS styles for specified components. + */ +async function main() { + const args = process.argv.slice(2); + + if (args.length === 0) { + console.error("Usage: node get_styles.mjs [Component2] ..."); + console.error("Example: node get_styles.mjs Button Card"); + process.exit(1); + } + + const components = args; + + // Try API first + console.error(`# Fetching styles for: ${components.join(", ")}...`); + const data = await fetchApi("/v1/components/styles", "POST", {components}); + + if (data && data.results) { + for (const result of data.results) { + result.source = "api"; + } + + // Output results + if (data.results.length === 1) { + const result = data.results[0]; + + if (result.stylesCode) { + console.log(`/* File: ${result.filePath || "unknown"} */`); + console.log(`/* GitHub: ${result.githubUrl || "unknown"} */`); + console.log(); + console.log(result.stylesCode); + } else { + console.log(JSON.stringify(result, null, 2)); + } + } else { + console.log(JSON.stringify(data, null, 2)); + } + + return; + } + + // Fallback to GitHub direct fetch + console.error("# API failed, using GitHub fallback..."); + const results = []; + + for (const component of components) { + const result = await fetchGithubFallback(component); + + results.push(result); + } + + if (results.length === 1) { + const result = results[0]; + + if (result.stylesCode) { + console.log(`/* File: ${result.filePath || "unknown"} */`); + console.log(`/* GitHub: ${result.githubUrl || "unknown"} */`); + console.log(); + console.log(result.stylesCode); + } else { + console.log(JSON.stringify(result, null, 2)); + } + } else { + console.log(JSON.stringify({results}, null, 2)); + } +} + +main(); diff --git a/.agents/skills/heroui-react/scripts/get_theme.mjs b/.agents/skills/heroui-react/scripts/get_theme.mjs new file mode 100644 index 0000000..32bfe5e --- /dev/null +++ b/.agents/skills/heroui-react/scripts/get_theme.mjs @@ -0,0 +1,177 @@ +#!/usr/bin/env node +/** + * Get theme variables and design tokens for HeroUI v3. + * + * Usage: + * node get_theme.mjs + * + * Output: + * Theme variables organized by common/light/dark with oklch color format + */ + +const API_BASE = process.env.HEROUI_API_BASE || "https://mcp-api.heroui.com"; +const APP_PARAM = "app=react-skills"; + +// Fallback theme reference when API is unavailable +const FALLBACK_THEME = { + common: { + base: [ + {name: "--font-sans", value: "ui-sans-serif, system-ui, sans-serif"}, + {name: "--font-mono", value: "ui-monospace, monospace"}, + {name: "--radius-sm", value: "0.375rem"}, + {name: "--radius-md", value: "0.5rem"}, + {name: "--radius-lg", value: "0.75rem"}, + {name: "--radius-full", value: "9999px"}, + ], + calculated: [{name: "--spacing-unit", value: "0.25rem"}], + }, + dark: { + semantic: [ + {name: "--color-background", value: "oklch(14.5% 0 0)"}, + {name: "--color-foreground", value: "oklch(98.4% 0 0)"}, + {name: "--color-accent", value: "oklch(55.1% 0.228 264.1)"}, + {name: "--color-danger", value: "oklch(63.7% 0.237 25.3)"}, + {name: "--color-success", value: "oklch(76.5% 0.177 163.2)"}, + {name: "--color-warning", value: "oklch(79.5% 0.184 86.0)"}, + ], + }, + latestVersion: "3.0.0-beta", + light: { + semantic: [ + {name: "--color-background", value: "oklch(100% 0 0)"}, + {name: "--color-foreground", value: "oklch(14.5% 0 0)"}, + {name: "--color-accent", value: "oklch(55.1% 0.228 264.1)"}, + {name: "--color-danger", value: "oklch(63.7% 0.237 25.3)"}, + {name: "--color-success", value: "oklch(76.5% 0.177 163.2)"}, + {name: "--color-warning", value: "oklch(79.5% 0.184 86.0)"}, + ], + }, + note: "This is a fallback. For complete theme variables, ensure the API is accessible.", + source: "fallback", + theme: "default", +}; + +/** + * Fetch data from HeroUI API with app parameter for analytics. + */ +async function fetchApi(endpoint) { + const separator = endpoint.includes("?") ? "&" : "?"; + const url = `${API_BASE}${endpoint}${separator}${APP_PARAM}`; + + try { + const response = await fetch(url, { + headers: {"User-Agent": "HeroUI-Skill/1.0"}, + signal: AbortSignal.timeout(30000), + }); + + if (!response.ok) { + console.error(`# API Error: HTTP ${response.status}`); + + return null; + } + + return await response.json(); + } catch (error) { + console.error(`# API Error: ${error.message}`); + + return null; + } +} + +/** + * Format theme variables for display. + */ +function formatVariables(variables) { + const lines = []; + + for (const variable of variables) { + const name = variable.name || ""; + const value = variable.value || ""; + const desc = variable.description || ""; + + if (desc) { + lines.push(` ${name}: ${value}; /* ${desc} */`); + } else { + lines.push(` ${name}: ${value};`); + } + } + + return lines.join("\n"); +} + +/** + * Main function to get theme variables. + */ +async function main() { + console.error("# Fetching theme variables..."); + + const rawData = await fetchApi("/v1/themes/variables?theme=default"); + + let data; + let version; + + if (!rawData) { + console.error("# API failed, using fallback theme reference..."); + data = FALLBACK_THEME; + version = FALLBACK_THEME.latestVersion || "unknown"; + } else { + // Handle API response format: { themes: [...], latestVersion: "..." } + if (rawData.themes && rawData.themes.length > 0) { + data = rawData.themes[0]; // Get first theme (default) + version = rawData.latestVersion || rawData.version || "unknown"; + } else { + // Direct format + data = rawData; + version = rawData.latestVersion || "unknown"; + } + } + + // Output as formatted CSS-like structure for readability + console.log("/* HeroUI v3 Theme Variables */"); + console.log(`/* Theme: ${data.theme || "default"} */`); + console.log(`/* Version: ${version} */`); + console.log(); + + // Common variables + if (data.common) { + console.log(":root {"); + console.log(" /* Base Variables */"); + if (data.common.base) { + console.log(formatVariables(data.common.base)); + } + console.log(); + console.log(" /* Calculated Variables */"); + if (data.common.calculated) { + console.log(formatVariables(data.common.calculated)); + } + console.log("}"); + console.log(); + } + + // Light mode + if (data.light) { + console.log(":root, [data-theme='light'] {"); + console.log(" /* Light Mode Semantic Variables */"); + if (data.light.semantic) { + console.log(formatVariables(data.light.semantic)); + } + console.log("}"); + console.log(); + } + + // Dark mode + if (data.dark) { + console.log("[data-theme='dark'] {"); + console.log(" /* Dark Mode Semantic Variables */"); + if (data.dark.semantic) { + console.log(formatVariables(data.dark.semantic)); + } + console.log("}"); + } + + // Also output raw JSON to stderr for programmatic use + console.error("\n# Raw JSON output:"); + console.error(JSON.stringify(rawData || data, null, 2)); +} + +main(); diff --git a/.agents/skills/heroui-react/scripts/list_components.mjs b/.agents/skills/heroui-react/scripts/list_components.mjs new file mode 100644 index 0000000..2278c8a --- /dev/null +++ b/.agents/skills/heroui-react/scripts/list_components.mjs @@ -0,0 +1,134 @@ +#!/usr/bin/env node +/** + * List all available HeroUI v3 components. + * + * Usage: + * node list_components.mjs + * + * Output: + * JSON with components array, latestVersion, and count + */ + +const API_BASE = process.env.HEROUI_API_BASE || "https://mcp-api.heroui.com"; +const APP_PARAM = "app=react-skills"; +const LLMS_TXT_URL = "https://v3.heroui.com/react/llms.txt"; + +/** + * Fetch data from HeroUI API with app parameter for analytics. + */ +async function fetchApi(endpoint) { + const separator = endpoint.includes("?") ? "&" : "?"; + const url = `${API_BASE}${endpoint}${separator}${APP_PARAM}`; + + try { + const response = await fetch(url, { + headers: {"User-Agent": "HeroUI-Skill/1.0"}, + signal: AbortSignal.timeout(30000), + }); + + if (!response.ok) { + console.error(`HTTP Error ${response.status}: ${response.statusText}`); + + return null; + } + + return await response.json(); + } catch (error) { + console.error(`API Error: ${error.message}`); + + return null; + } +} + +/** + * Fetch component list from llms.txt fallback URL. + */ +async function fetchFallback() { + try { + const response = await fetch(LLMS_TXT_URL, { + headers: {"User-Agent": "HeroUI-Skill/1.0"}, + signal: AbortSignal.timeout(30000), + }); + + if (!response.ok) { + return null; + } + + const content = await response.text(); + + // Parse markdown to extract component names from pattern: - [ComponentName](url) + // Look for links under the Components section (### Components) + const components = []; + let inComponentsSection = false; + + for (const line of content.split("\n")) { + // Check if we're entering the Components section (uses ### header) + if (line.trim() === "### Components") { + inComponentsSection = true; + continue; + } + + // Check if we're leaving the Components section (another ### header) + if (inComponentsSection && line.trim().startsWith("### ")) { + break; + } + + // Extract component name from markdown link pattern + // Match: - [ComponentName](https://v3.heroui.com/docs/react/components/component-name) + // Skip "All Components" which links to /components without a specific component + if (inComponentsSection) { + const match = line.match( + /^\s*-\s*\[([^\]]+)\]\(https:\/\/v3\.heroui\.com\/docs\/react\/components\/[a-z]/, + ); + + if (match) { + components.push(match[1]); + } + } + } + + if (components.length > 0) { + console.error(`# Using fallback: ${LLMS_TXT_URL}`); + + return { + components: components.sort(), + count: components.length, + latestVersion: "unknown", + }; + } + + return null; + } catch (error) { + console.error(`Fallback Error: ${error.message}`); + + return null; + } +} + +/** + * Main function to list all available HeroUI v3 components. + */ +async function main() { + let data = await fetchApi("/v1/components"); + + // Check if API returned valid data with components + if (!data || !data.components || data.components.length === 0) { + console.error("# API returned no components, trying fallback..."); + data = await fetchFallback(); + } + + if (!data || !data.components || data.components.length === 0) { + console.error("Error: Failed to fetch component list from API and fallback"); + process.exit(1); + } + + // Output formatted JSON + console.log(JSON.stringify(data, null, 2)); + + // Print summary to stderr for human readability + console.error( + `\n# Found ${data.components.length} components (${data.latestVersion || "unknown"})`, + ); +} + +main(); diff --git a/skills-lock.json b/skills-lock.json new file mode 100644 index 0000000..4d6e395 --- /dev/null +++ b/skills-lock.json @@ -0,0 +1,10 @@ +{ + "version": 1, + "skills": { + "heroui-react": { + "source": "heroui-inc/heroui", + "sourceType": "github", + "computedHash": "927aea70da0c060f930998e4c7373f790b87125798698e89f6431de689690f23" + } + } +}