`:
## Component API Patterns (Compound vs. Prop-Driven)
- **Compound Components (use subcomponents via `children`)**:
- - `Card`: `Card`, `CardHeader` (props: `title`, `description`, `action`), `CardContent`, `CardFooter`
+ - `Card`: `Card`, `CardHeader` (props: `leftIcon`, `action`), `CardTitle`, `CardDescription`, `CardContent`, `CardFooter`
+ - `BentoGrid`: `BentoGrid`, `BentoGridItem`
- `Tabs`: `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent`
- `Accordion`: `Accordion`, `AccordionItem`, `AccordionTrigger`, `AccordionContent`
- `Modal`: `Modal`, `ModalHeader`, `ModalContent`, `ModalFooter`
@@ -75,9 +76,14 @@ Apply on ``, ``, or any scoped container `
`:
- `Dropdown`: `Dropdown`, `DropdownTrigger`, `DropdownContent`, `DropdownItem`, `DropdownLabel`, `DropdownSeparator`
- `Popover`: `Popover`, `PopoverTrigger`, `PopoverContent`
- `Table`: `TableContainer`, `Table`, `TableHeader`, `TableBody`, `TableFooter`, `TableRow`, `TableHead`, `TableCell`, `TableCaption`
- - `Navbar`: `Navbar`, `NavbarBrand`, `NavbarLinks`, `NavbarLink`, `NavbarActions`
+ - `Navbar`: `Navbar`, `NavbarBrand`, `NavbarLinks`, `NavbarActions`
+ - `NavigationMenu`: `NavigationMenu`, `NavigationMenuList`, `NavigationMenuItem`, `NavigationMenuTrigger`, `NavigationMenuContent`, `NavigationMenuLink`, `NavigationMenuViewport`
+ - `Sidebar`: `SidebarProvider`, `Sidebar`, `SidebarTrigger`, `SidebarContent`, `SidebarHeader`, `SidebarSection`, `SidebarItem`, `SidebarSubList`, `SidebarSubItem`, `SidebarFooter`
- `Breadcrumb`: `Breadcrumb`, `BreadcrumbList`, `BreadcrumbItem`, `BreadcrumbSeparator`, `BreadcrumbEllipsis`
- `Stepper`: `Stepper`, `Step`
+ - `Dock`: `Dock`, `DockItem`
+ - `ButtonGroup`: `ButtonGroup`, `ButtonGroupItem`
+ - `Avatar`: `Avatar`, `AvatarGroup`
- **Prop-Driven Components (pass data via props, do NOT invent subcomponents)**:
- `Select`, `Combobox`: Pass `options={[{ value, label }]}`, `value`, `onChange`, `label`.
- `Input`, `Textarea`, `DatePicker`, `DateField`, `ColorPicker`, `CopyField`, `OtpInput`, `Checkbox`, `Radio`, `Switch`, `Slider`: Pass `label`, `description` / `helperText`, `error` directly as props.
@@ -86,7 +92,7 @@ Apply on ``, ``, or any scoped container `
`:
## Component Catalog
-Complete list of 55 production-ready components. For full API signatures, props tables, and code snippets, see [llms-full.txt](llms-full.txt).
+Complete list of 56 production-ready components. For full API signatures, props tables, and code snippets, see [llms-full.txt](llms-full.txt).
### Layout & Containers
diff --git a/public/registry.json b/public/registry.json
index e1e6564..79f2946 100644
--- a/public/registry.json
+++ b/public/registry.json
@@ -1529,7 +1529,9 @@
"toast",
"resetToasts"
],
- "propsDefinitions": []
+ "propsDefinitions": [
+ "export interface ToastProviderProps {\r\n children: React.ReactNode;\r\n position?: ToastPosition;\r\n invert?: boolean;\r\n}"
+ ]
},
"tooltip": {
"slug": "tooltip",
diff --git a/scripts/commands.ts b/scripts/commands.ts
index 10d4084..cf5a091 100644
--- a/scripts/commands.ts
+++ b/scripts/commands.ts
@@ -805,7 +805,7 @@ async function writeAiRulesFile(
'3. **Instance Overrides (`className` & `data-slot`)**: Use `className` (merged via `cn()`) or `data-slot` selectors (`className="[&_[data-slot=label]]:uppercase"`) for one-off tweaks.',
'',
'### Component API Patterns',
- '- **Compound (`children`)**: `Card` (`CardHeader`, `CardContent`, `CardFooter`), `Tabs` (`TabsList`, `TabsTrigger`, `TabsContent`), `Accordion` (`AccordionItem`, `AccordionTrigger`, `AccordionContent`), `Modal` (`ModalHeader`, `ModalContent`, `ModalFooter`), `Drawer` (`DrawerHeader`, `DrawerContent`, `DrawerFooter`), `Dropdown`, `Popover`, `Table`, `Navbar`, `Breadcrumb`, `Stepper`.',
+ '- **Compound (`children`)**: `Card` (`CardHeader`, `CardTitle`, `CardDescription`, `CardContent`, `CardFooter`), `BentoGrid` (`BentoGridItem`), `Tabs` (`TabsList`, `TabsTrigger`, `TabsContent`), `Accordion` (`AccordionItem`, `AccordionTrigger`, `AccordionContent`), `Modal` (`ModalHeader`, `ModalContent`, `ModalFooter`), `Drawer` (`DrawerHeader`, `DrawerContent`, `DrawerFooter`), `Dropdown`, `Popover`, `Table` (`TableContainer`, `TableHeader`, `TableBody`, `TableRow`, `TableHead`, `TableCell`), `Navbar` (`NavbarBrand`, `NavbarLinks`, `NavbarActions`), `NavigationMenu`, `Sidebar` (`SidebarProvider`, `SidebarHeader`, `SidebarContent`, `SidebarSection`, `SidebarItem`, `SidebarFooter`), `Breadcrumb`, `Stepper` (`Step`), `Dock` (`DockItem`), `ButtonGroup` (`ButtonGroupItem`), `Avatar` (`AvatarGroup`).',
'- **Prop-Driven (`options` / props)**: `Select` & `Combobox` use `options={[{ value, label }]}`; `Input`, `Textarea`, `DatePicker`, `DateField`, `ColorPicker`, `CopyField`, `OtpInput`, `Checkbox`, `Radio`, `Switch`, `Slider` take `label`, `description`/`helperText`, `error` as props; `StatsCard` takes `title`, `value`, `trend`, `status`, `chart`.',
'',
].join('\n');
diff --git a/scripts/generate-llms.ts b/scripts/generate-llms.ts
index 27cd47f..fac9b46 100644
--- a/scripts/generate-llms.ts
+++ b/scripts/generate-llms.ts
@@ -164,6 +164,113 @@ export function convertPropertyDocToMarkdown(docStr: string): string {
}
}
+function stripTopLevelExports(text: string): string {
+ const exportRegex = /^export\s+(?:const|function)\s+[A-Za-z0-9_]+/gm;
+ let result = '';
+ let lastIndex = 0;
+
+ while (true) {
+ exportRegex.lastIndex = lastIndex;
+ const match = exportRegex.exec(text);
+ if (!match) {
+ result += text.slice(lastIndex);
+ break;
+ }
+
+ result += text.slice(lastIndex, match.index);
+
+ let i = match.index;
+ let depth = 0;
+ let enteredBlock = false;
+ let inString: string | null = null;
+ let escaped = false;
+
+ while (i < text.length) {
+ const ch = text[i];
+
+ if (inString) {
+ if (escaped) {
+ escaped = false;
+ } else if (ch === '\\') {
+ escaped = true;
+ } else if (ch === inString) {
+ inString = null;
+ }
+ i++;
+ continue;
+ }
+
+ if (ch === '\'' || ch === '"' || ch === '`') {
+ inString = ch;
+ i++;
+ continue;
+ }
+
+ if (ch === '{' || ch === '(' || ch === '[') {
+ depth++;
+ enteredBlock = true;
+ } else if (ch === '}' || ch === ')' || ch === ']') {
+ depth = Math.max(0, depth - 1);
+ if (enteredBlock && depth === 0) {
+ i++;
+ if (text[i] === ';') i++;
+ break;
+ }
+ }
+ i++;
+ }
+
+ lastIndex = i;
+ }
+
+ return result;
+}
+
+function stripNotProseDivs(text: string): string {
+ const openRegex = /
]*\bnot-prose\b[^>]*>/g;
+ let result = '';
+ let lastIndex = 0;
+
+ while (true) {
+ openRegex.lastIndex = lastIndex;
+ const match = openRegex.exec(text);
+ if (!match) {
+ result += text.slice(lastIndex);
+ break;
+ }
+
+ result += text.slice(lastIndex, match.index);
+
+ let i = match.index + match[0].length;
+ let depth = 1;
+
+ while (i < text.length && depth > 0) {
+ const nextOpen = text.indexOf('
', i);
+
+ if (nextClose === -1) {
+ i = text.length;
+ break;
+ }
+
+ if (nextOpen !== -1 && nextOpen < nextClose) {
+ const after = text[nextOpen + 4];
+ if (after === '>' || after === ' ' || after === '\n' || after === '\r' || after === '\t') {
+ depth++;
+ }
+ i = nextOpen + 4;
+ } else {
+ depth--;
+ i = nextClose + 6;
+ }
+ }
+
+ lastIndex = i;
+ }
+
+ return result;
+}
+
export function cleanMdxForDocumentation(content: string): string {
const parts = content.split(/(```[\s\S]*?```)/g);
let removedTitleH1 = false;
@@ -176,8 +283,9 @@ export function cleanMdxForDocumentation(content: string): string {
// Outside code block
let text = parts[i];
- // Remove import statements
+ // Remove import statements and inline demo exports
text = text.replace(/^import\s+[\s\S]*?from\s+['"][^'"]+['"];?\s*$/gm, '');
+ text = stripTopLevelExports(text);
// Remove the top-level MDX H1 title (already emitted as `##
` by the generator)
if (!removedTitleH1 && /^#\s+.+$/m.test(text)) {
@@ -191,8 +299,9 @@ export function cleanMdxForDocumentation(content: string): string {
// to repeat across components, while H1/H2 anchors are strictly unique.
text = text.replace(/^(#{1,5})\s+/gm, (_match, hashes: string) => `#${hashes} `);
- // Remove ...
+ // Remove ... and ...
text = text.replace(//g, '');
+ text = stripNotProseDivs(text);
// Convert to Markdown tables
text = text.replace(/|<\/PropertyDoc>)/g, (match) => {
@@ -274,9 +383,12 @@ export function generateLlmsTxt(): string {
lines.push('2. **Tier 2 (Component-Wide Style)**: Edit `cva(...)` variants directly inside `src/dashkit//.tsx`.');
lines.push('3. **Tier 3 (One-Off Instance Override)**: Pass `className` (merged via `cn()`, which resolves `rounded-*` and `bg-none` against `ds-rounded*` and `ds-*-gradient`) or target sub-elements via `data-slot` (`className="[&_[data-slot=label]]:uppercase [&_[data-slot=trigger]]:rounded-full"`).\n');
+ const totalComponents = Object.keys(registry).length;
+
lines.push('## Component API Patterns (Compound vs. Prop-Driven)\n');
lines.push('- **Compound Components (use subcomponents via `children`)**:');
- lines.push(' - `Card`: `Card`, `CardHeader` (props: `title`, `description`, `action`), `CardContent`, `CardFooter`');
+ lines.push(' - `Card`: `Card`, `CardHeader` (props: `leftIcon`, `action`), `CardTitle`, `CardDescription`, `CardContent`, `CardFooter`');
+ lines.push(' - `BentoGrid`: `BentoGrid`, `BentoGridItem`');
lines.push(' - `Tabs`: `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent`');
lines.push(' - `Accordion`: `Accordion`, `AccordionItem`, `AccordionTrigger`, `AccordionContent`');
lines.push(' - `Modal`: `Modal`, `ModalHeader`, `ModalContent`, `ModalFooter`');
@@ -284,9 +396,14 @@ export function generateLlmsTxt(): string {
lines.push(' - `Dropdown`: `Dropdown`, `DropdownTrigger`, `DropdownContent`, `DropdownItem`, `DropdownLabel`, `DropdownSeparator`');
lines.push(' - `Popover`: `Popover`, `PopoverTrigger`, `PopoverContent`');
lines.push(' - `Table`: `TableContainer`, `Table`, `TableHeader`, `TableBody`, `TableFooter`, `TableRow`, `TableHead`, `TableCell`, `TableCaption`');
- lines.push(' - `Navbar`: `Navbar`, `NavbarBrand`, `NavbarLinks`, `NavbarLink`, `NavbarActions`');
+ lines.push(' - `Navbar`: `Navbar`, `NavbarBrand`, `NavbarLinks`, `NavbarActions`');
+ lines.push(' - `NavigationMenu`: `NavigationMenu`, `NavigationMenuList`, `NavigationMenuItem`, `NavigationMenuTrigger`, `NavigationMenuContent`, `NavigationMenuLink`, `NavigationMenuViewport`');
+ lines.push(' - `Sidebar`: `SidebarProvider`, `Sidebar`, `SidebarTrigger`, `SidebarContent`, `SidebarHeader`, `SidebarSection`, `SidebarItem`, `SidebarSubList`, `SidebarSubItem`, `SidebarFooter`');
lines.push(' - `Breadcrumb`: `Breadcrumb`, `BreadcrumbList`, `BreadcrumbItem`, `BreadcrumbSeparator`, `BreadcrumbEllipsis`');
lines.push(' - `Stepper`: `Stepper`, `Step`');
+ lines.push(' - `Dock`: `Dock`, `DockItem`');
+ lines.push(' - `ButtonGroup`: `ButtonGroup`, `ButtonGroupItem`');
+ lines.push(' - `Avatar`: `Avatar`, `AvatarGroup`');
lines.push('- **Prop-Driven Components (pass data via props, do NOT invent subcomponents)**:');
lines.push(' - `Select`, `Combobox`: Pass `options={[{ value, label }]}`, `value`, `onChange`, `label`.');
lines.push(' - `Input`, `Textarea`, `DatePicker`, `DateField`, `ColorPicker`, `CopyField`, `OtpInput`, `Checkbox`, `Radio`, `Switch`, `Slider`: Pass `label`, `description` / `helperText`, `error` directly as props.');
@@ -294,7 +411,7 @@ export function generateLlmsTxt(): string {
lines.push(' - `AreaChart`, `BarChart`, `LineChart`, `PieChart`: Pass `data` and `series` props.\n');
lines.push('## Component Catalog\n');
- lines.push('Complete list of 55 production-ready components. For full API signatures, props tables, and code snippets, see [llms-full.txt](llms-full.txt).\n');
+ lines.push(`Complete list of ${totalComponents} production-ready components. For full API signatures, props tables, and code snippets, see [llms-full.txt](llms-full.txt).\n`);
for (const cat of CATEGORIES) {
lines.push(`### ${cat.name}\n`);
diff --git a/scripts/tests/llms.test.ts b/scripts/tests/llms.test.ts
index 76af71a..01d1dc5 100644
--- a/scripts/tests/llms.test.ts
+++ b/scripts/tests/llms.test.ts
@@ -130,5 +130,21 @@ describe('LLMs Documentation Generator', () => {
expect(llmsContent).toContain('data-primary');
expect(llmsContent).toContain('3-Tier AI Customization Protocol');
expect(llmsContent).toContain('Component API Patterns (Compound vs. Prop-Driven)');
+ expect(llmsContent).not.toContain('`NavbarLink`');
+ expect(llmsContent).not.toContain('CardHeader (props: title, description');
+ });
+
+ it('should strip inline demo exports and not-prose preview blocks from llms-full.txt', () => {
+ const llmsFullContent = generateLlmsFullTxt();
+ expect(llmsFullContent).not.toContain('SelectDemo');
+ expect(llmsFullContent).not.toContain('not-prose');
+ });
+
+ it('should extract propsDefinitions for every registered component including toast', () => {
+ const { registryJsonPath } = buildLlmsFiles();
+ const registryJson = JSON.parse(fs.readFileSync(registryJsonPath, 'utf8'));
+ for (const [slug, entry] of Object.entries<{ propsDefinitions: string[] }>(registryJson.components)) {
+ expect(entry.propsDefinitions.length, `Missing propsDefinitions for ${slug}`).toBeGreaterThan(0);
+ }
});
});
diff --git a/src/dashkit/Toast/Toast.tsx b/src/dashkit/Toast/Toast.tsx
index d66deae..2800a21 100644
--- a/src/dashkit/Toast/Toast.tsx
+++ b/src/dashkit/Toast/Toast.tsx
@@ -21,15 +21,17 @@ const viewportPositionClasses: Record = {
'bottom-center': 'bottom-8 left-1/2 -translate-x-1/2 items-center',
};
+export interface ToastProviderProps {
+ children: React.ReactNode;
+ position?: ToastPosition;
+ invert?: boolean;
+}
+
export function ToastProvider({
children,
position = 'bottom-right',
invert = false
-}: {
- children: React.ReactNode;
- position?: ToastPosition;
- invert?: boolean;
-}) {
+}: ToastProviderProps) {
const { toasts } = useToast();
const isMounted = React.useSyncExternalStore(
React.useCallback(() => () => {}, []),
diff --git a/src/layouts/MdxWrapper.tsx b/src/layouts/MdxWrapper.tsx
index dabe273..3775ded 100644
--- a/src/layouts/MdxWrapper.tsx
+++ b/src/layouts/MdxWrapper.tsx
@@ -270,11 +270,15 @@ function extractMarkdownFromDom(root: HTMLElement): string {
lines.push(`\n#### ${node.textContent?.trim() ?? ''}\n`);
} else if (tag === 'p') {
lines.push(`${node.textContent?.trim() ?? ''}\n`);
+ } else if (tag === 'blockquote') {
+ const quoteLines = (node.textContent?.trim() ?? '').split('\n').map((l) => `> ${l.trim()}`);
+ lines.push(`${quoteLines.join('\n')}\n`);
} else if (tag === 'ul' || tag === 'ol') {
const items = Array.from(node.querySelectorAll(':scope > li'));
- for (const li of items) {
- lines.push(`- ${li.textContent?.trim() ?? ''}`);
- }
+ items.forEach((li, idx) => {
+ const prefix = tag === 'ol' ? `${idx + 1}.` : '-';
+ lines.push(`${prefix} ${li.textContent?.trim() ?? ''}`);
+ });
lines.push('');
} else if (node.querySelector('pre')) {
const pre = node.querySelector('pre');
diff --git a/src/pages/ai.mdx b/src/pages/ai.mdx
index e2dd5b8..e15aaeb 100644
--- a/src/pages/ai.mdx
+++ b/src/pages/ai.mdx
@@ -8,8 +8,8 @@ Feed Dashkit's structured documentation directly into your AI assistant's contex
| Resource | Path | Purpose |
| :--- | :--- | :--- |
-| **LLM Index** | [`/llms.txt`](/llms.txt) | Compact architecture overview, token reference, CLI commands, and 55-component catalog. |
-| **Full Technical Context** | [`/llms-full.txt`](/llms-full.txt) | Complete single-file specification with installation commands, JSX examples, and Markdown props tables for all 55 components. |
+| **LLM Index** | [`/llms.txt`](/llms.txt) | Compact architecture overview, token reference, CLI commands, and 56-component catalog. |
+| **Full Technical Context** | [`/llms-full.txt`](/llms-full.txt) | Complete single-file specification with installation commands, JSX examples, and Markdown props tables for all 56 components. |
| **JSON Registry Schema** | [`/registry.json`](/registry.json) | Machine-readable JSON metadata containing component slugs, file paths, NPM dependencies, registry dependencies, exports, and raw TypeScript `*Props` interfaces. |
## Automatic AI Rules (`dashkit ai-rules`)
@@ -34,7 +34,7 @@ Autonomous agents can query the Dashkit CLI in non-interactive mode using `--jso
# Inspect exports, dependencies, and TypeScript Props for a component
bunx carllosnc/dashkit info card --json
-# List all 55 components and check which ones are installed locally
+# List all 56 components and check which ones are installed locally
bunx carllosnc/dashkit list --json
# Check if any local component files were customized vs. upstream
@@ -90,16 +90,22 @@ Dashkit components follow two predictable composition patterns:
Use exported sub-components for structural layouts and overlays:
- **Card**: `Card`, `CardHeader`, `CardTitle`, `CardDescription`, `CardContent`, `CardFooter`
+- **BentoGrid**: `BentoGrid`, `BentoGridItem`
- **Tabs**: `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent`
- **Accordion**: `Accordion`, `AccordionItem`, `AccordionTrigger`, `AccordionContent`
- **Modal**: `Modal`, `ModalHeader`, `ModalContent`, `ModalFooter`
- **Drawer**: `Drawer`, `DrawerHeader`, `DrawerContent`, `DrawerFooter`
- **Dropdown**: `Dropdown`, `DropdownTrigger`, `DropdownContent`, `DropdownItem`, `DropdownLabel`, `DropdownSeparator`
- **Popover**: `Popover`, `PopoverTrigger`, `PopoverContent`
-- **Table**: `Table`, `TableHeader`, `TableBody`, `TableFooter`, `TableRow`, `TableHead`, `TableCell`, `TableCaption`
+- **Table**: `TableContainer`, `Table`, `TableHeader`, `TableBody`, `TableFooter`, `TableRow`, `TableHead`, `TableCell`, `TableCaption`
- **Navbar**: `Navbar`, `NavbarBrand`, `NavbarLinks`, `NavbarActions`
+- **NavigationMenu**: `NavigationMenu`, `NavigationMenuList`, `NavigationMenuItem`, `NavigationMenuTrigger`, `NavigationMenuContent`, `NavigationMenuLink`, `NavigationMenuViewport`
- **Breadcrumb**: `Breadcrumb`, `BreadcrumbList`, `BreadcrumbItem`, `BreadcrumbSeparator`, `BreadcrumbEllipsis`
-- **Sidebar**: `Sidebar`, `SidebarHeader`, `SidebarSection`, `SidebarItem`, `SidebarFooter`
+- **Sidebar**: `SidebarProvider`, `Sidebar`, `SidebarTrigger`, `SidebarContent`, `SidebarHeader`, `SidebarSection`, `SidebarItem`, `SidebarSubList`, `SidebarSubItem`, `SidebarFooter`
+- **Stepper**: `Stepper`, `Step`
+- **Dock**: `Dock`, `DockItem`
+- **ButtonGroup**: `ButtonGroup`, `ButtonGroupItem`
+- **Avatar**: `Avatar`, `AvatarGroup`
### 2. Prop-Driven Components (Configure via Props)
diff --git a/src/pages/cli.mdx b/src/pages/cli.mdx
index 05e86d5..7c73db8 100644
--- a/src/pages/cli.mdx
+++ b/src/pages/cli.mdx
@@ -26,7 +26,7 @@ Add one or multiple components in a single command. The CLI resolves recursive D
bunx carllosnc/dashkit add button card input modal
```
-To install all 55 components at once:
+To install all 56 components at once:
```bash
bunx carllosnc/dashkit add --all
@@ -67,7 +67,7 @@ Copies one or more components and their `registryDependencies` into your project
bunx carllosnc/dashkit add [components...] [options]
```
-- `-a, --all`: Add all 55 components in the registry.
+- `-a, --all`: Add all 56 components in the registry.
- `-o, --output `: Target directory (defaults to `componentsDir` in `dashkit.json` or `src/dashkit`).
- `--no-install`: Skip automatic installation of missing NPM dependencies.
- `-f, --force`: Overwrite existing local component files without prompting.