drivn 1.9.0 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +763 -29
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import {Command}from'commander';import*as n from'@clack/prompts';import m from'picocolors';import {join,dirname}from'path';import {execSync}from'child_process';import {existsSync,readFileSync,writeFileSync,mkdirSync}from'fs';import {McpServer}from'@modelcontextprotocol/sdk/server/mcp.js';import {StdioServerTransport}from'@modelcontextprotocol/sdk/server/stdio.js';import {z as z$1}from'zod';var z={next:"Next.js",react:"React"};function $(e){let r=join(e,"package.json");if(!existsSync(r))throw new Error("package.json not found");let t=JSON.parse(readFileSync(r,"utf-8")),a={...t.dependencies,...t.devDependencies},l="react";a.next&&(l="next");let d=existsSync(join(e,"src")),p=existsSync(join(e,"tsconfig.json"));return {framework:l,srcDir:d,typescript:p}}var G="drivn.config.json";function U(e){let r=join(e,G);return existsSync(r)?JSON.parse(readFileSync(r,"utf-8")):null}function K(e,r){let t=join(e,G);writeFileSync(t,JSON.stringify(r,null,2));}function Ae(e){existsSync(e)||mkdirSync(e,{recursive:true});}function b(e,r){Ae(dirname(e)),writeFileSync(e,r);}function H(e){return readFileSync(e,"utf-8")}function x(e){return existsSync(e)}function I(e){return existsSync(join(e,"pnpm-lock.yaml"))?"pnpm":"npm"}function R(e,r){let t=r.join(" ");return e==="pnpm"?`pnpm add ${t}`:`npm install ${t}`}function W(e){return e==="pnpm"?"pnpm dlx":"npx"}var C=`@import "tailwindcss";
2
+ import {Command}from'commander';import*as n from'@clack/prompts';import u from'picocolors';import {join,dirname}from'path';import {execSync}from'child_process';import {existsSync,readFileSync,writeFileSync,mkdirSync}from'fs';import {McpServer}from'@modelcontextprotocol/sdk/server/mcp.js';import {StdioServerTransport}from'@modelcontextprotocol/sdk/server/stdio.js';import {z as z$1}from'zod';var z={next:"Next.js",react:"React"};function V(e){let r=join(e,"package.json");if(!existsSync(r))throw new Error("package.json not found");let t=JSON.parse(readFileSync(r,"utf-8")),a={...t.dependencies,...t.devDependencies},l="react";a.next&&(l="next");let d=existsSync(join(e,"src")),p=existsSync(join(e,"tsconfig.json"));return {framework:l,srcDir:d,typescript:p}}var F="drivn.config.json";function U(e){let r=join(e,F);return existsSync(r)?JSON.parse(readFileSync(r,"utf-8")):null}function K(e,r){let t=join(e,F);writeFileSync(t,JSON.stringify(r,null,2));}function Ve(e){existsSync(e)||mkdirSync(e,{recursive:true});}function b(e,r){Ve(dirname(e)),writeFileSync(e,r);}function H(e){return readFileSync(e,"utf-8")}function x(e){return existsSync(e)}function E(e){return existsSync(join(e,"pnpm-lock.yaml"))?"pnpm":"npm"}function w(e,r){let t=r.join(" ");return e==="pnpm"?`pnpm add ${t}`:`npm install ${t}`}function W(e){return e==="pnpm"?"pnpm dlx":"npx"}var C=`@import "tailwindcss";
3
3
 
4
4
  :root {
5
5
  /* Surfaces */
@@ -136,13 +136,13 @@ body {
136
136
  /* Special Surfaces */
137
137
  --overlay: hsl(0 0% 0% / 0.18);
138
138
  }
139
- `;var ze=`import { type ClassValue, clsx } from 'clsx'
139
+ `;var Fe=`import { type ClassValue, clsx } from 'clsx'
140
140
  import { twMerge } from 'tailwind-merge'
141
141
 
142
142
  export function cn(...inputs: ClassValue[]) {
143
143
  return twMerge(clsx(inputs))
144
144
  }
145
- `,$e=["src/app/globals.css","src/styles/globals.css","src/styles/globals.scss","app/globals.css"];function Ve(e){for(let r of $e)if(x(join(e,r)))return r;return null}async function Y(){let e=process.cwd();console.log(""),console.log(m.bgCyan(m.bold(m.black(" Drivn "))));let r;try{r=$(e),n.log.success(`Detected ${m.cyan(z[r.framework])}`);}catch{n.log.error("No package.json found. Run this command in a project directory."),n.outro("Setup cancelled"),process.exit(1);}if(x(join(e,"drivn.config.json"))){let s=await n.confirm({message:"Config already exists. Overwrite?",initialValue:false});(n.isCancel(s)||!s)&&(n.cancel("Setup cancelled"),process.exit(0));}let t=r.srcDir?"src/components/ui":"components/ui",a=r.srcDir?"src/utils":"utils",l=await n.group({components:()=>n.text({message:"Where should components be installed?",placeholder:t,defaultValue:t}),utils:()=>n.text({message:"Where should utilities be placed?",placeholder:a,defaultValue:a})},{onCancel:()=>{n.cancel("Setup cancelled"),process.exit(0);}}),d={framework:r.framework,typescript:r.typescript,paths:{components:l.components,utils:l.utils},installed:[]},p=r.typescript?"ts":"js",f=join(e,l.utils,`cn.${p}`);x(f)||b(f,ze);let g=Ve(e);if(g){let s=await n.confirm({message:`Found ${m.cyan(g)}. Add Drivn color tokens?`,initialValue:true});!n.isCancel(s)&&s&&(b(join(e,g),C),d.paths.globals=g,n.log.success(`Color tokens written to ${m.cyan(g)}`));}else {let s=r.srcDir?"src/styles/globals.css":"styles/globals.css",i=await n.text({message:"Where should the globals CSS file be created?",placeholder:s,defaultValue:s});n.isCancel(i)||(b(join(e,i),C),d.paths.globals=i,n.log.success(`Color tokens written to ${m.cyan(i)}`));}K(e,d);let N=I(e),u=W(N),h=["clsx","tailwind-merge","lucide-react"],o=n.spinner();o.start("Installing dependencies");try{execSync(R(N,h),{cwd:e,stdio:"ignore"}),o.stop("Dependencies installed");}catch{o.stop("Failed to install dependencies"),n.log.warn(`Run manually: ${R(N,h)}`);}n.log.info(`Add components with: ${m.cyan(`${u} drivn add button`)}`),n.log.info(`Add dark/light theme: ${m.cyan(`${u} drivn add theme`)}`),n.outro("Drivn initialized");}var q=`'use client'
145
+ `,Ue=["src/app/globals.css","src/styles/globals.css","src/styles/globals.scss","app/globals.css"];function Ke(e){for(let r of Ue)if(x(join(e,r)))return r;return null}async function Y(){let e=process.cwd();console.log(""),console.log(u.bgCyan(u.bold(u.black(" Drivn "))));let r;try{r=V(e),n.log.success(`Detected ${u.cyan(z[r.framework])}`);}catch{n.log.error("No package.json found. Run this command in a project directory."),n.outro("Setup cancelled"),process.exit(1);}if(x(join(e,"drivn.config.json"))){let s=await n.confirm({message:"Config already exists. Overwrite?",initialValue:false});(n.isCancel(s)||!s)&&(n.cancel("Setup cancelled"),process.exit(0));}let t=r.srcDir?"src/components/ui":"components/ui",a=r.srcDir?"src/utils":"utils",l=await n.group({components:()=>n.text({message:"Where should components be installed?",placeholder:t,defaultValue:t}),utils:()=>n.text({message:"Where should utilities be placed?",placeholder:a,defaultValue:a})},{onCancel:()=>{n.cancel("Setup cancelled"),process.exit(0);}}),d={framework:r.framework,typescript:r.typescript,paths:{components:l.components,utils:l.utils},installed:[]},p=r.typescript?"ts":"js",f=join(e,l.utils,`cn.${p}`);x(f)||b(f,Fe);let g=Ke(e);if(g){let s=await n.confirm({message:`Found ${u.cyan(g)}. Add Drivn color tokens?`,initialValue:true});!n.isCancel(s)&&s&&(b(join(e,g),C),d.paths.globals=g,n.log.success(`Color tokens written to ${u.cyan(g)}`));}else {let s=r.srcDir?"src/styles/globals.css":"styles/globals.css",i=await n.text({message:"Where should the globals CSS file be created?",placeholder:s,defaultValue:s});n.isCancel(i)||(b(join(e,i),C),d.paths.globals=i,n.log.success(`Color tokens written to ${u.cyan(i)}`));}K(e,d);let N=E(e),m=W(N),h=["clsx","tailwind-merge","lucide-react"],o=n.spinner();o.start("Installing dependencies");try{execSync(w(N,h),{cwd:e,stdio:"ignore"}),o.stop("Dependencies installed");}catch{o.stop("Failed to install dependencies"),n.log.warn(`Run manually: ${w(N,h)}`);}n.log.info(`Add components with: ${u.cyan(`${m} drivn add button`)}`),n.log.info(`Add dark/light theme: ${u.cyan(`${m} drivn add theme`)}`),n.outro("Drivn initialized");}var X=`'use client'
146
146
 
147
147
  import { ThemeProvider as NextThemesProvider } from 'next-themes'
148
148
 
@@ -161,7 +161,7 @@ export function ThemeProvider({
161
161
  </NextThemesProvider>
162
162
  )
163
163
  }
164
- `;var J=`'use client'
164
+ `;var q=`'use client'
165
165
 
166
166
  import * as React from 'react'
167
167
  import { ChevronDown } from 'lucide-react'
@@ -293,7 +293,7 @@ export const Accordion = Object.assign(AccordionRoot, {
293
293
  Trigger,
294
294
  Content
295
295
  })
296
- `;var X=`import * as React from 'react'
296
+ `;var J=`import * as React from 'react'
297
297
  import { cn } from '@/utils/cn'
298
298
 
299
299
  const styles = {
@@ -1646,6 +1646,515 @@ export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(({
1646
1646
  Checkbox.displayName = 'Checkbox'
1647
1647
  `;var le=`'use client'
1648
1648
 
1649
+ import * as React from 'react'
1650
+ import { Command as CommandPrimitive } from 'cmdk'
1651
+ import { ChevronDown, X, Check, Search } from 'lucide-react'
1652
+ import { cn } from '@/utils/cn'
1653
+
1654
+ const styles = {
1655
+ base: 'relative',
1656
+ trigger: {
1657
+ base: cn(
1658
+ 'flex items-center justify-between w-full min-h-10',
1659
+ 'px-3 gap-2',
1660
+ 'border border-input rounded-[10px] text-sm',
1661
+ 'focus:outline-none transition-colors',
1662
+ 'cursor-pointer'
1663
+ ),
1664
+ singleText: 'flex-1 truncate text-left',
1665
+ multiText: cn(
1666
+ 'flex flex-1 flex-wrap items-center gap-1',
1667
+ 'min-h-[36px] py-1'
1668
+ ),
1669
+ placeholder: 'text-muted-foreground',
1670
+ chevron: cn(
1671
+ 'w-4 h-4 shrink-0 text-muted-foreground',
1672
+ 'transition-transform duration-200'
1673
+ ),
1674
+ clear: cn(
1675
+ 'w-4 h-4 shrink-0 text-muted-foreground',
1676
+ 'hover:text-foreground transition-colors'
1677
+ ),
1678
+ },
1679
+ tag: {
1680
+ base: cn(
1681
+ 'inline-flex items-center gap-1 px-2 py-0.5',
1682
+ 'text-xs rounded-md bg-muted text-foreground'
1683
+ ),
1684
+ remove: cn(
1685
+ 'w-3 h-3 text-muted-foreground',
1686
+ 'hover:text-foreground cursor-pointer'
1687
+ ),
1688
+ },
1689
+ content: cn(
1690
+ 'absolute top-full left-0 right-0 mt-1 z-50',
1691
+ 'bg-card border border-border rounded-[10px]',
1692
+ 'shadow-lg overflow-hidden',
1693
+ 'transition-[opacity,scale] duration-150 ease-out'
1694
+ ),
1695
+ input: {
1696
+ wrapper: cn(
1697
+ 'flex items-center gap-2 px-3',
1698
+ 'border-b border-border'
1699
+ ),
1700
+ icon: 'w-4 h-4 shrink-0 text-muted-foreground',
1701
+ field: cn(
1702
+ 'flex h-10 w-full bg-transparent py-2',
1703
+ 'text-sm text-foreground outline-none',
1704
+ 'placeholder:text-muted-foreground'
1705
+ ),
1706
+ },
1707
+ list: cn(
1708
+ 'max-h-[200px] overflow-y-auto p-1',
1709
+ '[&_[cmdk-list-sizer]]:space-y-0.5'
1710
+ ),
1711
+ empty: cn(
1712
+ 'py-6 text-center text-sm',
1713
+ 'text-muted-foreground'
1714
+ ),
1715
+ group: cn(
1716
+ 'overflow-hidden',
1717
+ '[&_[cmdk-group-heading]]:px-1.5',
1718
+ '[&_[cmdk-group-heading]]:py-1.5',
1719
+ '[&_[cmdk-group-heading]]:text-xs',
1720
+ '[&_[cmdk-group-heading]]:font-medium',
1721
+ '[&_[cmdk-group-heading]]:text-muted-foreground'
1722
+ ),
1723
+ label: cn(
1724
+ 'px-1.5 py-1.5 text-xs font-medium',
1725
+ 'text-muted-foreground'
1726
+ ),
1727
+ item: cn(
1728
+ 'relative flex items-center gap-2 px-2.5 py-1.5',
1729
+ 'text-sm rounded-lg cursor-default select-none',
1730
+ 'data-[selected=true]:bg-accent',
1731
+ 'data-[disabled=true]:pointer-events-none',
1732
+ 'data-[disabled=true]:opacity-50'
1733
+ ),
1734
+ icon: 'w-4 h-4 shrink-0 text-primary ml-auto',
1735
+ separator: 'w-full h-px bg-border',
1736
+ }
1737
+
1738
+ type IconProp = React.ComponentType<{ className?: string }> | React.ReactElement
1739
+
1740
+ interface ComboboxCtx {
1741
+ open: boolean
1742
+ setOpen: (v: boolean) => void
1743
+ multiple: boolean
1744
+ value: string | string[]
1745
+ onSelect: (v: string) => void
1746
+ onClear: () => void
1747
+ }
1748
+
1749
+ function ComboboxRoot({
1750
+ children,
1751
+ value,
1752
+ onChange,
1753
+ multiple = false,
1754
+ className,
1755
+ }: {
1756
+ children: React.ReactNode
1757
+ value?: string | string[]
1758
+ onChange?: (value: string | string[]) => void
1759
+ multiple?: boolean
1760
+ className?: string
1761
+ }) {
1762
+ const [open, setOpen] = React.useState(false)
1763
+ const ref = React.useRef<HTMLDivElement>(null)
1764
+ const close = React.useCallback(() => setOpen(false), [])
1765
+
1766
+ const onSelect = React.useCallback(
1767
+ (v: string) => {
1768
+ if (multiple) {
1769
+ const arr = (value as string[]) ?? []
1770
+ const next = arr.includes(v)
1771
+ ? arr.filter((i) => i !== v)
1772
+ : [...arr, v]
1773
+ onChange?.(next)
1774
+ } else {
1775
+ onChange?.(v)
1776
+ close()
1777
+ }
1778
+ },
1779
+ [multiple, value, onChange, close]
1780
+ )
1781
+
1782
+ const onClear = React.useCallback(() => {
1783
+ onChange?.(multiple ? [] : '')
1784
+ }, [multiple, onChange])
1785
+
1786
+ React.useEffect(() => {
1787
+ const onClick = (e: MouseEvent) => {
1788
+ if (!ref.current?.contains(e.target as Node))
1789
+ close()
1790
+ }
1791
+ document.addEventListener('mousedown', onClick)
1792
+ return () =>
1793
+ document.removeEventListener('mousedown', onClick)
1794
+ }, [close])
1795
+
1796
+ return (
1797
+ <Ctx.Provider
1798
+ value={{
1799
+ open,
1800
+ setOpen,
1801
+ multiple,
1802
+ value: value ?? (multiple ? [] : ''),
1803
+ onSelect,
1804
+ onClear,
1805
+ }}
1806
+ >
1807
+ <div ref={ref} className={cn(styles.base, className)}>
1808
+ {children}
1809
+ </div>
1810
+ </Ctx.Provider>
1811
+ )
1812
+ }
1813
+
1814
+ function Trigger({
1815
+ placeholder = 'Select...',
1816
+ clearable = false,
1817
+ children,
1818
+ className,
1819
+ ...props
1820
+ }: {
1821
+ placeholder?: string
1822
+ clearable?: boolean
1823
+ children?: React.ReactNode
1824
+ className?: string
1825
+ } & Omit<
1826
+ React.ButtonHTMLAttributes<HTMLButtonElement>,
1827
+ 'onClick'
1828
+ >) {
1829
+ const { open, setOpen, multiple, value, onSelect, onClear } = useCombobox()
1830
+
1831
+ const values = Array.isArray(value) ? value : []
1832
+
1833
+ const hasValue = multiple
1834
+ ? values.length > 0
1835
+ : (value as string) !== ''
1836
+
1837
+ return (
1838
+ <button
1839
+ className={cn(styles.trigger.base, className)}
1840
+ onClick={() => setOpen(!open)}
1841
+ {...props}
1842
+ >
1843
+ {multiple ? (
1844
+ <span className={styles.trigger.multiText}>
1845
+ {values.length === 0 ? (
1846
+ <span className={styles.trigger.placeholder}>
1847
+ {placeholder}
1848
+ </span>
1849
+ ) : (
1850
+ <>
1851
+ {values.map((v) => (
1852
+ <span key={v} className={styles.tag.base}>
1853
+ {v}
1854
+ <X
1855
+ className={styles.tag.remove}
1856
+ onMouseDown={(e) => {
1857
+ e.stopPropagation()
1858
+ e.preventDefault()
1859
+ onSelect(v)
1860
+ }}
1861
+ />
1862
+ </span>
1863
+ ))}
1864
+ </>
1865
+ )}
1866
+ </span>
1867
+ ) : (
1868
+ <span
1869
+ className={cn(
1870
+ styles.trigger.singleText,
1871
+ !hasValue && styles.trigger.placeholder
1872
+ )}
1873
+ >
1874
+ {hasValue ? (children ?? value) : placeholder}
1875
+ </span>
1876
+ )}
1877
+ {clearable && hasValue ? (
1878
+ <X
1879
+ className={styles.trigger.clear}
1880
+ onMouseDown={(e) => {
1881
+ e.stopPropagation()
1882
+ e.preventDefault()
1883
+ onClear()
1884
+ }}
1885
+ />
1886
+ ) : (
1887
+ <ChevronDown className={cn(styles.trigger.chevron, open && 'rotate-180')} />
1888
+ )}
1889
+ </button>
1890
+ )
1891
+ }
1892
+
1893
+ function Content({
1894
+ placeholder = 'Search...',
1895
+ children,
1896
+ className,
1897
+ }: {
1898
+ placeholder?: string
1899
+ children: React.ReactNode
1900
+ className?: string
1901
+ }) {
1902
+ const { open } = useCombobox()
1903
+
1904
+ return (
1905
+ <div
1906
+ className={cn(
1907
+ styles.content,
1908
+ open ? 'opacity-100 scale-100' : 'opacity-0 scale-95 pointer-events-none',
1909
+ className
1910
+ )}
1911
+ >
1912
+ <CommandPrimitive key={open ? 'open' : 'closed'} shouldFilter>
1913
+ <div className={styles.input.wrapper}>
1914
+ <Search className={styles.input.icon} />
1915
+ <CommandPrimitive.Input
1916
+ autoFocus
1917
+ className={styles.input.field}
1918
+ placeholder={placeholder}
1919
+ />
1920
+ </div>
1921
+ <CommandPrimitive.List className={styles.list}>
1922
+ {children}
1923
+ </CommandPrimitive.List>
1924
+ </CommandPrimitive>
1925
+ </div>
1926
+ )
1927
+ }
1928
+
1929
+ function Empty({
1930
+ children,
1931
+ className,
1932
+ }: {
1933
+ children?: React.ReactNode
1934
+ className?: string
1935
+ }) {
1936
+ return (
1937
+ <CommandPrimitive.Empty className={cn(styles.empty, className)}>
1938
+ {children ?? 'No results found.'}
1939
+ </CommandPrimitive.Empty>
1940
+ )
1941
+ }
1942
+
1943
+ function Group({
1944
+ heading,
1945
+ children,
1946
+ className,
1947
+ }: {
1948
+ heading?: string
1949
+ children: React.ReactNode
1950
+ className?: string
1951
+ }) {
1952
+ return (
1953
+ <CommandPrimitive.Group
1954
+ heading={heading}
1955
+ className={cn(styles.group, className)}
1956
+ >
1957
+ {children}
1958
+ </CommandPrimitive.Group>
1959
+ )
1960
+ }
1961
+
1962
+ function ComboboxLabel({
1963
+ children,
1964
+ className,
1965
+ }: {
1966
+ children: React.ReactNode
1967
+ className?: string
1968
+ }) {
1969
+ return (
1970
+ <div className={cn(styles.label, className)}>
1971
+ {children}
1972
+ </div>
1973
+ )
1974
+ }
1975
+
1976
+ function Item({
1977
+ value: itemValue,
1978
+ icon: Icon,
1979
+ disabled,
1980
+ children,
1981
+ className,
1982
+ ...props
1983
+ }: {
1984
+ value: string
1985
+ icon?: IconProp
1986
+ disabled?: boolean
1987
+ children: React.ReactNode
1988
+ className?: string
1989
+ } & Omit<
1990
+ React.ComponentProps<typeof CommandPrimitive.Item>,
1991
+ 'onSelect' | 'value'
1992
+ >) {
1993
+ const { value, onSelect, multiple } = useCombobox()
1994
+ const isSelected = multiple
1995
+ ? (value as string[]).includes(itemValue)
1996
+ : value === itemValue
1997
+
1998
+ return (
1999
+ <CommandPrimitive.Item
2000
+ value={itemValue}
2001
+ disabled={disabled}
2002
+ onSelect={() => onSelect(itemValue)}
2003
+ className={cn(styles.item, className)}
2004
+ {...props}
2005
+ >
2006
+ {Icon && (React.isValidElement(Icon) ? Icon : <Icon className="w-4 h-4" />)}
2007
+ {children}
2008
+ {isSelected && (
2009
+ <Check className={styles.icon} />
2010
+ )}
2011
+ </CommandPrimitive.Item>
2012
+ )
2013
+ }
2014
+
2015
+ function ComboboxSeparator({
2016
+ className,
2017
+ }: {
2018
+ className?: string
2019
+ }) {
2020
+ return (
2021
+ <CommandPrimitive.Separator className={cn(styles.separator, className)} />
2022
+ )
2023
+ }
2024
+
2025
+ const Ctx = React.createContext<ComboboxCtx | null>(null)
2026
+
2027
+ function useCombobox() {
2028
+ const ctx = React.useContext(Ctx)
2029
+ if (!ctx) throw new Error('Combobox compound used outside <Combobox>')
2030
+ return ctx
2031
+ }
2032
+
2033
+ export const Combobox = Object.assign(ComboboxRoot, {
2034
+ Trigger,
2035
+ Content,
2036
+ Empty,
2037
+ Group,
2038
+ Label: ComboboxLabel,
2039
+ Item,
2040
+ Separator: ComboboxSeparator,
2041
+ })
2042
+ `;var de=`'use client'
2043
+
2044
+ import * as React from 'react'
2045
+ import { cn } from '@/utils/cn'
2046
+
2047
+ const styles = {
2048
+ panel: 'grid transition-[grid-template-rows] duration-200',
2049
+ content: 'overflow-hidden',
2050
+ }
2051
+
2052
+ interface CollapsibleCtx {
2053
+ open: boolean
2054
+ toggle: () => void
2055
+ triggerId: string
2056
+ contentId: string
2057
+ }
2058
+
2059
+ function CollapsibleRoot({
2060
+ children,
2061
+ className,
2062
+ defaultOpen = false,
2063
+ open: controlledOpen,
2064
+ onOpenChange,
2065
+ }: {
2066
+ children: React.ReactNode
2067
+ className?: string
2068
+ defaultOpen?: boolean
2069
+ open?: boolean
2070
+ onOpenChange?: (open: boolean) => void
2071
+ }) {
2072
+ const [internalOpen, setInternalOpen] = React.useState(defaultOpen)
2073
+ const open = controlledOpen ?? internalOpen
2074
+ const id = React.useId()
2075
+ const triggerId = \`\${id}trigger\`
2076
+ const contentId = \`\${id}content\`
2077
+
2078
+ const toggle = () => {
2079
+ const next = !open
2080
+ if (controlledOpen === undefined) setInternalOpen(next)
2081
+ onOpenChange?.(next)
2082
+ }
2083
+
2084
+ return (
2085
+ <Ctx.Provider value={{ open, toggle, triggerId, contentId }}>
2086
+ <div
2087
+ className={className}
2088
+ data-state={open ? 'open' : 'closed'}
2089
+ >
2090
+ {children}
2091
+ </div>
2092
+ </Ctx.Provider>
2093
+ )
2094
+ }
2095
+
2096
+ function Trigger({
2097
+ children,
2098
+ className,
2099
+ ...props
2100
+ }: {
2101
+ children: React.ReactNode
2102
+ className?: string
2103
+ } & React.ButtonHTMLAttributes<HTMLButtonElement>) {
2104
+ const { open, toggle, triggerId, contentId } = useCtx()
2105
+ return (
2106
+ <button
2107
+ id={triggerId}
2108
+ aria-expanded={open}
2109
+ aria-controls={contentId}
2110
+ data-state={open ? 'open' : 'closed'}
2111
+ className={className}
2112
+ onClick={toggle}
2113
+ {...props}
2114
+ >
2115
+ {children}
2116
+ </button>
2117
+ )
2118
+ }
2119
+
2120
+ function Content({
2121
+ children,
2122
+ className,
2123
+ }: {
2124
+ children: React.ReactNode
2125
+ className?: string
2126
+ }) {
2127
+ const { open, triggerId, contentId } = useCtx()
2128
+ return (
2129
+ <div
2130
+ id={contentId}
2131
+ role="region"
2132
+ aria-labelledby={triggerId}
2133
+ data-state={open ? 'open' : 'closed'}
2134
+ className={styles.panel}
2135
+ style={{ gridTemplateRows: open ? '1fr' : '0fr' }}
2136
+ >
2137
+ <div className={cn(styles.content, className)}>
2138
+ {children}
2139
+ </div>
2140
+ </div>
2141
+ )
2142
+ }
2143
+
2144
+ const Ctx = React.createContext<CollapsibleCtx | null>(null)
2145
+
2146
+ function useCtx() {
2147
+ const c = React.useContext(Ctx)
2148
+ if (!c) throw new Error('Collapsible compound used outside <Collapsible>')
2149
+ return c
2150
+ }
2151
+
2152
+ export const Collapsible = Object.assign(CollapsibleRoot, {
2153
+ Trigger,
2154
+ Content,
2155
+ })
2156
+ `;var pe=`'use client'
2157
+
1649
2158
  import * as React from 'react'
1650
2159
  import { X } from 'lucide-react'
1651
2160
  import { cn } from '@/utils/cn'
@@ -1800,7 +2309,7 @@ export const Dialog = Object.assign(DialogRoot, {
1800
2309
  Trigger,
1801
2310
  Content,
1802
2311
  })
1803
- `;var de=`'use client'
2312
+ `;var ue=`'use client'
1804
2313
 
1805
2314
  import * as React from 'react'
1806
2315
  import { X } from 'lucide-react'
@@ -2019,7 +2528,7 @@ export const Drawer = Object.assign(DrawerRoot, {
2019
2528
  Header,
2020
2529
  Footer,
2021
2530
  })
2022
- `;var pe=`'use client'
2531
+ `;var me=`'use client'
2023
2532
 
2024
2533
  import * as React from 'react'
2025
2534
  import { cn } from '@/utils/cn'
@@ -2217,7 +2726,7 @@ export const Dropdown = Object.assign(DropdownRoot, {
2217
2726
  Label,
2218
2727
  Separator: DropdownSeparator
2219
2728
  })
2220
- `;var me=`import * as React from 'react'
2729
+ `;var fe=`import * as React from 'react'
2221
2730
  import { cn } from '@/utils/cn'
2222
2731
 
2223
2732
  const styles = {
@@ -2240,7 +2749,7 @@ export const Input = React.forwardRef<HTMLInputElement, InputProps>(({
2240
2749
  )
2241
2750
 
2242
2751
  Input.displayName = 'Input'
2243
- `;var ue=`import * as React from 'react'
2752
+ `;var ge=`import * as React from 'react'
2244
2753
  import { cn } from '@/utils/cn'
2245
2754
 
2246
2755
  const styles = {
@@ -2297,7 +2806,7 @@ function Group({
2297
2806
  }
2298
2807
 
2299
2808
  export const Kbd = Object.assign(KbdRoot, { Group })
2300
- `;var fe=`import * as React from 'react'
2809
+ `;var he=`import * as React from 'react'
2301
2810
  import { cn } from '@/utils/cn'
2302
2811
 
2303
2812
  export function Label({
@@ -2314,7 +2823,7 @@ export function Label({
2314
2823
  />
2315
2824
  )
2316
2825
  }
2317
- `;var ge=`import * as React from 'react'
2826
+ `;var be=`import * as React from 'react'
2318
2827
  import { ChevronLeft, ChevronRight, MoreHorizontal } from 'lucide-react'
2319
2828
  import { cn } from '@/utils/cn'
2320
2829
 
@@ -2447,7 +2956,7 @@ export const Pagination = Object.assign(PaginationRoot, {
2447
2956
  Next,
2448
2957
  Ellipsis: PaginationEllipsis,
2449
2958
  })
2450
- `;var he=`'use client'
2959
+ `;var ve=`'use client'
2451
2960
 
2452
2961
  import * as React from 'react'
2453
2962
  import { cn } from '@/utils/cn'
@@ -2552,7 +3061,7 @@ export const Popover = Object.assign(PopoverRoot, {
2552
3061
  Trigger,
2553
3062
  Content
2554
3063
  })
2555
- `;var be=`import * as React from 'react'
3064
+ `;var xe=`import * as React from 'react'
2556
3065
  import { cn } from '@/utils/cn'
2557
3066
 
2558
3067
  const styles = {
@@ -2590,7 +3099,7 @@ export function Progress({
2590
3099
  </div>
2591
3100
  )
2592
3101
  }
2593
- `;var ve=`'use client'
3102
+ `;var ye=`'use client'
2594
3103
 
2595
3104
  import * as React from 'react'
2596
3105
  import { cn } from '@/utils/cn'
@@ -2739,7 +3248,7 @@ function useRadioGroup() {
2739
3248
  }
2740
3249
 
2741
3250
  export const RadioGroup = Object.assign(RadioGroupRoot, { Item })
2742
- `;var xe=`'use client'
3251
+ `;var Ne=`'use client'
2743
3252
 
2744
3253
  import * as React from 'react'
2745
3254
  import { ChevronDown } from 'lucide-react'
@@ -2893,7 +3402,53 @@ export const Select = Object.assign(SelectRoot, {
2893
3402
  Menu,
2894
3403
  Option
2895
3404
  })
2896
- `;var ye=`import * as React from 'react'
3405
+ `;var Ce=`import * as React from 'react'
3406
+ import { cn } from '@/utils/cn'
3407
+
3408
+ const styles = {
3409
+ base: cn(
3410
+ 'relative [scrollbar-width:thin]',
3411
+ '[scrollbar-color:var(--border)_transparent]',
3412
+ '[&::-webkit-scrollbar]:w-1.5',
3413
+ '[&::-webkit-scrollbar]:h-1.5',
3414
+ '[&::-webkit-scrollbar-track]:bg-transparent',
3415
+ '[&::-webkit-scrollbar-track]:rounded-full',
3416
+ '[&::-webkit-scrollbar-thumb]:rounded-full',
3417
+ '[&::-webkit-scrollbar-thumb]:bg-border',
3418
+ '[&:hover::-webkit-scrollbar-thumb]:bg-muted-foreground/30',
3419
+ '[&::-webkit-scrollbar-corner]:bg-transparent'
3420
+ ),
3421
+ orientations: {
3422
+ vertical: 'overflow-y-auto overflow-x-hidden',
3423
+ horizontal: 'overflow-x-auto overflow-y-hidden',
3424
+ },
3425
+ }
3426
+
3427
+ interface ScrollAreaProps
3428
+ extends React.HTMLAttributes<HTMLDivElement> {
3429
+ orientation?: 'vertical' | 'horizontal'
3430
+ }
3431
+
3432
+ export function ScrollArea({
3433
+ orientation = 'vertical',
3434
+ className,
3435
+ children,
3436
+ ...props
3437
+ }: ScrollAreaProps) {
3438
+ return (
3439
+ <div
3440
+ className={cn(
3441
+ styles.base,
3442
+ styles.orientations[orientation],
3443
+ className
3444
+ )}
3445
+ {...props}
3446
+ >
3447
+ {children}
3448
+ </div>
3449
+ )
3450
+ }
3451
+ `;var Re=`import * as React from 'react'
2897
3452
  import { cn } from '@/utils/cn'
2898
3453
 
2899
3454
  const styles = {
@@ -2917,7 +3472,24 @@ export function Separator({
2917
3472
  />
2918
3473
  )
2919
3474
  }
2920
- `;var Ne=`'use client'
3475
+ `;var we=`import * as React from 'react'
3476
+ import { cn } from '@/utils/cn'
3477
+
3478
+ export function Skeleton({
3479
+ className,
3480
+ ...props
3481
+ }: React.HTMLAttributes<HTMLDivElement>) {
3482
+ return (
3483
+ <div
3484
+ className={cn(
3485
+ 'bg-muted/80 rounded-md animate-skeleton',
3486
+ className
3487
+ )}
3488
+ {...props}
3489
+ />
3490
+ )
3491
+ }
3492
+ `;var ke=`'use client'
2921
3493
 
2922
3494
  import * as React from 'react'
2923
3495
  import { ChevronDown, PanelLeft } from 'lucide-react'
@@ -3203,7 +3775,7 @@ export const Sidebar = Object.assign(SidebarRoot, {
3203
3775
  Separator: SidebarSeparator,
3204
3776
  CollapseButton,
3205
3777
  })
3206
- `;var Ce=`'use client'
3778
+ `;var Pe=`'use client'
3207
3779
 
3208
3780
  import * as React from 'react'
3209
3781
  import { cn } from '@/utils/cn'
@@ -3348,7 +3920,7 @@ export const Slider = React.forwardRef<HTMLInputElement, SliderProps>(({
3348
3920
  )
3349
3921
 
3350
3922
  Slider.displayName = 'Slider'
3351
- `;var we=`'use client'
3923
+ `;var Se=`'use client'
3352
3924
 
3353
3925
  import * as React from 'react'
3354
3926
  import { cn } from '@/utils/cn'
@@ -3389,7 +3961,7 @@ export function Switch({
3389
3961
  </button>
3390
3962
  )
3391
3963
  }
3392
- `;var Re=`'use client'
3964
+ `;var De=`'use client'
3393
3965
 
3394
3966
  import * as React from 'react'
3395
3967
  import { cn } from '@/utils/cn'
@@ -3503,7 +4075,7 @@ export const Tabs = Object.assign(TabsRoot, {
3503
4075
  Tab,
3504
4076
  Panel
3505
4077
  })
3506
- `;var ke=`import * as React from 'react'
4078
+ `;var Te=`import * as React from 'react'
3507
4079
  import { cn } from '@/utils/cn'
3508
4080
 
3509
4081
  const styles = {
@@ -3530,7 +4102,7 @@ export const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(({
3530
4102
  ))
3531
4103
 
3532
4104
  Textarea.displayName = 'Textarea'
3533
- `;var Pe=`'use client'
4105
+ `;var Ie=`'use client'
3534
4106
 
3535
4107
  import * as React from 'react'
3536
4108
  import { Toaster as Sonner, toast } from 'sonner'
@@ -3573,7 +4145,169 @@ function Toaster() {
3573
4145
  }
3574
4146
 
3575
4147
  export { Toaster, toast }
3576
- `;var De=`import * as React from 'react'
4148
+ `;var Ee=`'use client'
4149
+
4150
+ import * as React from 'react'
4151
+ import { cn } from '@/utils/cn'
4152
+
4153
+ const styles = {
4154
+ base: cn(
4155
+ 'inline-flex items-center justify-center gap-2',
4156
+ 'rounded-md text-sm font-medium',
4157
+ 'transition-colors cursor-pointer',
4158
+ 'focus:outline-none',
4159
+ 'disabled:opacity-50 disabled:cursor-default'
4160
+ ),
4161
+ variants: {
4162
+ default: cn(
4163
+ 'bg-transparent text-muted-foreground',
4164
+ 'hover:bg-muted hover:text-foreground',
4165
+ 'data-[state=on]:bg-muted',
4166
+ 'data-[state=on]:text-foreground'
4167
+ ),
4168
+ outline: cn(
4169
+ 'border border-border bg-transparent',
4170
+ 'text-muted-foreground',
4171
+ 'hover:border-foreground/20',
4172
+ 'data-[state=on]:bg-muted',
4173
+ 'data-[state=on]:text-foreground',
4174
+ 'data-[state=on]:border-muted'
4175
+ ),
4176
+ },
4177
+ sizes: {
4178
+ sm: 'h-8 px-2.5 text-xs',
4179
+ md: 'h-9 px-3 text-sm',
4180
+ lg: 'h-10 px-4 text-sm',
4181
+ },
4182
+ group: 'inline-flex items-center gap-1',
4183
+ vertical: 'flex-col',
4184
+ }
4185
+
4186
+ interface ToggleGroupCtx {
4187
+ type: 'single' | 'multiple'
4188
+ value: string | string[]
4189
+ onToggle: (v: string) => void
4190
+ disabled?: boolean
4191
+ }
4192
+
4193
+ interface ToggleProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'value' | 'onChange'> {
4194
+ pressed?: boolean
4195
+ defaultPressed?: boolean
4196
+ onChange?: (pressed: boolean) => void
4197
+ value?: string
4198
+ variant?: keyof typeof styles.variants
4199
+ size?: keyof typeof styles.sizes
4200
+ }
4201
+
4202
+ const ToggleButton = React.forwardRef<HTMLButtonElement, ToggleProps>(({
4203
+ pressed,
4204
+ defaultPressed,
4205
+ onChange,
4206
+ value,
4207
+ variant = 'default',
4208
+ size = 'md',
4209
+ disabled,
4210
+ className,
4211
+ children,
4212
+ ...props
4213
+ }, ref) => {
4214
+ const ctx = React.useContext(Ctx)
4215
+ const [internal, setInternal] = React.useState(defaultPressed ?? false)
4216
+ const inGroup = ctx !== null && value !== undefined
4217
+
4218
+ const isPressed = inGroup
4219
+ ? ctx.type === 'multiple'
4220
+ ? (ctx.value as string[]).includes(value)
4221
+ : ctx.value === value
4222
+ : pressed ?? internal
4223
+
4224
+ return (
4225
+ <button
4226
+ ref={ref}
4227
+ type="button"
4228
+ aria-pressed={isPressed}
4229
+ data-state={isPressed ? 'on' : 'off'}
4230
+ disabled={disabled ?? (inGroup ? ctx.disabled : false)}
4231
+ className={cn(styles.base, styles.variants[variant], styles.sizes[size], className)}
4232
+ onClick={() => {
4233
+ if (inGroup) {
4234
+ ctx.onToggle(value)
4235
+ } else {
4236
+ const next = !isPressed
4237
+ if (pressed === undefined) setInternal(next)
4238
+ onChange?.(next)
4239
+ }
4240
+ }}
4241
+ {...props}
4242
+ >
4243
+ {children}
4244
+ </button>
4245
+ )
4246
+ })
4247
+
4248
+ ToggleButton.displayName = 'Toggle'
4249
+
4250
+ function ToggleGroupRoot({
4251
+ children,
4252
+ type,
4253
+ defaultValue,
4254
+ value: controlledValue,
4255
+ onValueChange,
4256
+ orientation = 'horizontal',
4257
+ disabled,
4258
+ className,
4259
+ }: {
4260
+ children: React.ReactNode
4261
+ type: 'single' | 'multiple'
4262
+ defaultValue?: string | string[]
4263
+ value?: string | string[]
4264
+ onValueChange?: (value: string | string[]) => void
4265
+ orientation?: 'vertical' | 'horizontal'
4266
+ disabled?: boolean
4267
+ className?: string
4268
+ }) {
4269
+ const [internal, setInternal] = React.useState<string | string[]>(defaultValue ?? (type === 'multiple' ? [] : ''))
4270
+ const isControlled = controlledValue !== undefined
4271
+ const current = isControlled ? controlledValue : internal
4272
+
4273
+ const onToggle = (itemValue: string) => {
4274
+ let next: string | string[]
4275
+ if (type === 'single') {
4276
+ next = current === itemValue ? '' : itemValue
4277
+ } else {
4278
+ const arr = current as string[]
4279
+ next = arr.includes(itemValue)
4280
+ ? arr.filter((v) => v !== itemValue)
4281
+ : [...arr, itemValue]
4282
+ }
4283
+ if (!isControlled) setInternal(next)
4284
+ onValueChange?.(next)
4285
+ }
4286
+
4287
+ return (
4288
+ <Ctx.Provider
4289
+ value={{ type, value: current, onToggle, disabled }}
4290
+ >
4291
+ <div
4292
+ role="group"
4293
+ className={cn(
4294
+ styles.group,
4295
+ orientation === 'vertical' && styles.vertical,
4296
+ className
4297
+ )}
4298
+ >
4299
+ {children}
4300
+ </div>
4301
+ </Ctx.Provider>
4302
+ )
4303
+ }
4304
+
4305
+ const Ctx = React.createContext<ToggleGroupCtx | null>(null)
4306
+
4307
+ export const Toggle = Object.assign(ToggleButton, {
4308
+ Group: ToggleGroupRoot,
4309
+ })
4310
+ `;var Le=`import * as React from 'react'
3577
4311
  import { cn } from '@/utils/cn'
3578
4312
 
3579
4313
  const styles = {
@@ -3747,7 +4481,7 @@ export const Table = Object.assign(TableRoot, {
3747
4481
  Head,
3748
4482
  Cell,
3749
4483
  })
3750
- `;var Se=`import * as React from 'react'
4484
+ `;var Oe=`import * as React from 'react'
3751
4485
  import { cn } from '@/utils/cn'
3752
4486
 
3753
4487
  const styles = {
@@ -3792,7 +4526,7 @@ export function Tooltip({
3792
4526
  </span>
3793
4527
  )
3794
4528
  }
3795
- `;var Te=`
4529
+ `;var Me=`
3796
4530
  /* react-day-picker theme integration */
3797
4531
  .rdp-root {
3798
4532
  --rdp-accent-color: var(--primary);
@@ -3803,7 +4537,7 @@ export function Tooltip({
3803
4537
  --rdp-selected-border: none;
3804
4538
  --rdp-day_button-border: none;
3805
4539
  }
3806
- `,v=[{name:"accordion",description:"Collapsible content sections with dot notation and smooth animation",dependencies:[],npmDependencies:[]},{name:"alert",description:"Contextual feedback messages with variants and icons",dependencies:[],npmDependencies:[]},{name:"aspect-ratio",description:"Maintain consistent width-to-height ratios for images, videos, and embedded content",dependencies:[],npmDependencies:[]},{name:"avatar",description:"User avatar with image support and fallback initials",dependencies:[],npmDependencies:[]},{name:"badge",description:"Small status indicator with color variants",dependencies:[],npmDependencies:[]},{name:"breadcrumb",description:"Breadcrumb navigation with auto-separators, ellipsis, and dot notation",dependencies:[],npmDependencies:[]},{name:"button",description:"Button with variants, sizes, loading state, and icon slots",dependencies:[],npmDependencies:[]},{name:"calendar",description:"Date picker powered by react-day-picker with single, range, and dropdown modes",dependencies:[],npmDependencies:["react-day-picker"]},{name:"date-picker",description:"Date picker input with Calendar dropdown for single date and range selection",dependencies:["calendar"],npmDependencies:[]},{name:"command",description:"Searchable command menu with filtering, keyboard navigation, and dialog mode",dependencies:["dialog"],npmDependencies:["cmdk"]},{name:"carousel",description:"Carousel with touch/swipe, navigation arrows, dot indicators, and loop mode",dependencies:["button"],npmDependencies:["embla-carousel-react"]},{name:"card",description:"Container with dot notation preview and info sub-components",dependencies:[],npmDependencies:[]},{name:"checkbox",description:"Checkbox input with label and CSS-only checkmark",dependencies:[],npmDependencies:[]},{name:"dialog",description:"Modal dialog with dot notation, overlay, and escape key",dependencies:["button"],npmDependencies:[]},{name:"drawer",description:"Slide-in panel with side positioning, header/footer, and overlay",dependencies:["button"],npmDependencies:[]},{name:"dropdown",description:"Dropdown menu with dot notation, groups, separators, and click-outside",dependencies:["button"],npmDependencies:[]},{name:"input",description:"Text input with focus ring and disabled state",dependencies:[],npmDependencies:[]},{name:"kbd",description:"Keyboard key display for shortcuts and hotkeys",dependencies:[],npmDependencies:[]},{name:"label",description:"Accessible form label for inputs, checkboxes, and selects",dependencies:[],npmDependencies:[]},{name:"pagination",description:"Page navigation with dot notation, Previous/Next, ellipsis, and active state",dependencies:[],npmDependencies:[]},{name:"popover",description:"Floating content panel with dot notation and click-outside",dependencies:["button"],npmDependencies:[]},{name:"progress",description:"Progress bar with animated fill and ARIA attributes",dependencies:[],npmDependencies:[]},{name:"radio-group",description:"Radio group with dot notation, orientation support, and controlled/uncontrolled selection",dependencies:[],npmDependencies:[]},{name:"select",description:"Custom select with dot notation and composable options",dependencies:[],npmDependencies:[]},{name:"separator",description:"Visual divider with horizontal and vertical orientation",dependencies:[],npmDependencies:[]},{name:"sidebar",description:"Collapsible sidebar with dot notation, icon items, groups, and layout variants",dependencies:[],npmDependencies:[]},{name:"slider",description:"Range slider with pointer drag, step snapping, and size variants",dependencies:[],npmDependencies:[]},{name:"switch",description:"Toggle switch with smooth transition",dependencies:[],npmDependencies:[]},{name:"tabs",description:"Tab navigation with dot notation and panel content",dependencies:[],npmDependencies:[]},{name:"table",description:"Data table with dot notation, striped/bordered variants, and responsive overflow",dependencies:[],npmDependencies:[]},{name:"textarea",description:"Multi-line text input with consistent styling",dependencies:[],npmDependencies:[]},{name:"theme",description:"Dark/light theme support with next-themes and ThemeProvider",dependencies:[],npmDependencies:["next-themes"]},{name:"toast",description:"Toast notifications powered by Sonner",dependencies:[],npmDependencies:["sonner"]},{name:"tooltip",description:"Pure CSS tooltip with 4 position options",dependencies:[],npmDependencies:[]}],w={accordion:J,alert:X,"aspect-ratio":Z,avatar:Q,badge:ee,breadcrumb:te,button:ne,calendar:oe,"date-picker":re,command:se,carousel:ae,card:ie,checkbox:ce,dialog:le,drawer:de,dropdown:pe,input:me,kbd:ue,label:fe,pagination:ge,popover:he,progress:be,"radio-group":ve,select:xe,separator:ye,sidebar:Ne,slider:Ce,switch:we,table:De,tabs:Re,textarea:ke,theme:q,toast:Pe,tooltip:Se};async function Ee(e){let r=process.cwd();n.intro("drivn add");let t=U(r);if(t||(n.log.error("Drivn is not initialized. Run npx drivn@latest create"),n.outro("Cancelled"),process.exit(1)),!e||!e.length){let o=await n.multiselect({message:"Select components to add",options:v.map(s=>({label:s.name,hint:s.description,value:s.name})),required:true});n.isCancel(o)&&(n.cancel("Cancelled"),process.exit(0)),e=o;}let a=e.filter(o=>!v.find(s=>s.name===o));a.length&&(n.log.error(`Unknown components: ${a.join(", ")}`),n.log.info("Available: "+v.map(o=>o.name).join(", ")),n.outro("Cancelled"),process.exit(1));let l=e.includes("theme"),d=e.filter(o=>o!=="theme"),p=new Set,f=new Set,g=o=>{if(p.has(o))return;let s=v.find(i=>i.name===o);s&&(s.dependencies.forEach(i=>g(i)),s.npmDependencies?.forEach(i=>f.add(i)),p.add(o));};d.forEach(g),l&&f.add("next-themes");let N=[...p].filter(o=>!d.includes(o));N.length&&n.log.info(`Required dependency: ${N.join(", ")}`);let u=t.typescript?"tsx":"jsx",h=join(r,t.paths.components);for(let o of p){let s=join(h,`${o}.${u}`);if(x(s)){let D=await n.confirm({message:`${o}.${u} exists. Overwrite?`,initialValue:false});if(n.isCancel(D)||!D){n.log.warn(`Skipped ${o}`);continue}}let i=w[o];i=i.replace(/@\/utils/g,`@/${t.paths.utils.replace(/^src\//,"")}`),b(s,i),n.log.success(`${o} \u2192 ${t.paths.components}/${o}.${u}`);}if(l){let o=join(h,`theme-provider.${u}`);if(x(o)){let i=await n.confirm({message:`theme-provider.${u} exists. Overwrite?`,initialValue:false});!n.isCancel(i)&&i?(b(o,w.theme),n.log.success(`theme-provider \u2192 ${t.paths.components}/theme-provider.${u}`)):n.log.warn("Skipped theme-provider");}else b(o,w.theme),n.log.success(`theme-provider \u2192 ${t.paths.components}/theme-provider.${u}`);if(t.paths.globals){let i=join(r,t.paths.globals);if(x(i)){let D=H(i);D.includes('[data-theme="dark"]')?n.log.warn("Theme tokens already exist in globals \u2014 skipped"):(b(i,D+k),n.log.success(`Theme tokens appended to ${m.cyan(t.paths.globals)}`));}else n.log.warn(`Globals file not found at ${t.paths.globals}`);}else n.log.warn('No globals path in drivn.config.json. Add "globals" to paths');let s=t.paths.components.replace(/^src\//,"@/");n.log.message(""),n.log.info(m.bold("Complete the setup:")),n.log.message(""),n.log.message(m.bold(`${m.cyan("1.")} Import ThemeProvider in your root layout:`)),n.log.message(m.cyan(` import { ThemeProvider } from "${s}/theme-provider"`)),n.log.message(""),n.log.message(m.bold(`${m.cyan("2.")} Add suppressHydrationWarning to <html>:`)),n.log.message(m.cyan(" <html suppressHydrationWarning>")),n.log.message(""),n.log.message(m.bold(`${m.cyan("3.")} Wrap your app with ThemeProvider:`)),n.log.message(m.cyan(" <ThemeProvider>")),n.log.message(m.cyan(" {children}")),n.log.message(m.cyan(" </ThemeProvider>")),n.log.message("");}if(p.has("calendar")&&t.paths.globals){let o=join(r,t.paths.globals);if(x(o)){let s=H(o);s.includes(".rdp-root")?n.log.warn("Calendar tokens already exist in globals \u2014 skipped"):(b(o,s+Te),n.log.success(`Calendar tokens appended to ${m.cyan(t.paths.globals)}`));}}if(f.size){let o=I(r),s=[...f],i=n.spinner();i.start("Installing packages");try{execSync(R(o,s),{cwd:r,stdio:"ignore"}),i.stop("Packages installed");}catch{i.stop("Failed to install packages"),n.log.warn(`Run manually: ${R(o,s)}`);}}n.outro("Done.");}var j=`# Drivn Component Conventions
4540
+ `,v=[{name:"accordion",description:"Collapsible content sections with dot notation and smooth animation",dependencies:[],npmDependencies:[]},{name:"alert",description:"Contextual feedback messages with variants and icons",dependencies:[],npmDependencies:[]},{name:"aspect-ratio",description:"Maintain consistent width-to-height ratios for images, videos, and embedded content",dependencies:[],npmDependencies:[]},{name:"avatar",description:"User avatar with image support and fallback initials",dependencies:[],npmDependencies:[]},{name:"badge",description:"Small status indicator with color variants",dependencies:[],npmDependencies:[]},{name:"breadcrumb",description:"Breadcrumb navigation with auto-separators, ellipsis, and dot notation",dependencies:[],npmDependencies:[]},{name:"button",description:"Button with variants, sizes, loading state, and icon slots",dependencies:[],npmDependencies:[]},{name:"calendar",description:"Date picker powered by react-day-picker with single, range, and dropdown modes",dependencies:[],npmDependencies:["react-day-picker"]},{name:"date-picker",description:"Date picker input with Calendar dropdown for single date and range selection",dependencies:["calendar"],npmDependencies:[]},{name:"command",description:"Searchable command menu with filtering, keyboard navigation, and dialog mode",dependencies:["dialog"],npmDependencies:["cmdk"]},{name:"carousel",description:"Carousel with touch/swipe, navigation arrows, dot indicators, and loop mode",dependencies:["button"],npmDependencies:["embla-carousel-react"]},{name:"card",description:"Container with dot notation preview and info sub-components",dependencies:[],npmDependencies:[]},{name:"checkbox",description:"Checkbox input with label and CSS-only checkmark",dependencies:[],npmDependencies:[]},{name:"combobox",description:"Searchable select with filtering, single/multi selection, and keyboard navigation",dependencies:[],npmDependencies:["cmdk"]},{name:"collapsible",description:"Toggle content visibility with smooth animation and accessible controls",dependencies:[],npmDependencies:[]},{name:"dialog",description:"Modal dialog with dot notation, overlay, and escape key",dependencies:["button"],npmDependencies:[]},{name:"drawer",description:"Slide-in panel with side positioning, header/footer, and overlay",dependencies:["button"],npmDependencies:[]},{name:"dropdown",description:"Dropdown menu with dot notation, groups, separators, and click-outside",dependencies:["button"],npmDependencies:[]},{name:"input",description:"Text input with focus ring and disabled state",dependencies:[],npmDependencies:[]},{name:"kbd",description:"Keyboard key display for shortcuts and hotkeys",dependencies:[],npmDependencies:[]},{name:"label",description:"Accessible form label for inputs, checkboxes, and selects",dependencies:[],npmDependencies:[]},{name:"pagination",description:"Page navigation with dot notation, Previous/Next, ellipsis, and active state",dependencies:[],npmDependencies:[]},{name:"popover",description:"Floating content panel with dot notation and click-outside",dependencies:["button"],npmDependencies:[]},{name:"progress",description:"Progress bar with animated fill and ARIA attributes",dependencies:[],npmDependencies:[]},{name:"radio-group",description:"Radio group with dot notation, orientation support, and controlled/uncontrolled selection",dependencies:[],npmDependencies:[]},{name:"select",description:"Custom select with dot notation and composable options",dependencies:[],npmDependencies:[]},{name:"scroll-area",description:"Themed scrollable container with custom scrollbar styling and orientation control",dependencies:[],npmDependencies:[]},{name:"separator",description:"Visual divider with horizontal and vertical orientation",dependencies:[],npmDependencies:[]},{name:"skeleton",description:"Loading placeholder with pulse animation, sized and shaped via className",dependencies:[],npmDependencies:[]},{name:"sidebar",description:"Collapsible sidebar with dot notation, icon items, groups, and layout variants",dependencies:[],npmDependencies:[]},{name:"slider",description:"Range slider with pointer drag, step snapping, and size variants",dependencies:[],npmDependencies:[]},{name:"switch",description:"Toggle switch with smooth transition",dependencies:[],npmDependencies:[]},{name:"tabs",description:"Tab navigation with dot notation and panel content",dependencies:[],npmDependencies:[]},{name:"table",description:"Data table with dot notation, striped/bordered variants, and responsive overflow",dependencies:[],npmDependencies:[]},{name:"textarea",description:"Multi-line text input with consistent styling",dependencies:[],npmDependencies:[]},{name:"theme",description:"Dark/light theme support with next-themes and ThemeProvider",dependencies:[],npmDependencies:["next-themes"]},{name:"toast",description:"Toast notifications powered by Sonner",dependencies:[],npmDependencies:["sonner"]},{name:"toggle",description:"Toggle button with pressed state, single and multiple selection groups",dependencies:[],npmDependencies:[]},{name:"tooltip",description:"Pure CSS tooltip with 4 position options",dependencies:[],npmDependencies:[]}],R={accordion:q,alert:J,"aspect-ratio":Z,avatar:Q,badge:ee,breadcrumb:te,button:ne,calendar:oe,"date-picker":re,command:se,carousel:ae,card:ie,checkbox:ce,combobox:le,collapsible:de,dialog:pe,drawer:ue,dropdown:me,input:fe,kbd:ge,label:he,pagination:be,popover:ve,progress:xe,"radio-group":ye,select:Ne,"scroll-area":Ce,separator:Re,skeleton:we,sidebar:ke,slider:Pe,switch:Se,table:Le,tabs:De,textarea:Te,theme:X,toast:Ie,toggle:Ee,tooltip:Oe};async function Ae(e){let r=process.cwd();n.intro("drivn add");let t=U(r);if(t||(n.log.error("Drivn is not initialized. Run npx drivn@latest create"),n.outro("Cancelled"),process.exit(1)),!e||!e.length){let o=await n.multiselect({message:"Select components to add",options:v.map(s=>({label:s.name,hint:s.description,value:s.name})),required:true});n.isCancel(o)&&(n.cancel("Cancelled"),process.exit(0)),e=o;}let a=e.filter(o=>!v.find(s=>s.name===o));a.length&&(n.log.error(`Unknown components: ${a.join(", ")}`),n.log.info("Available: "+v.map(o=>o.name).join(", ")),n.outro("Cancelled"),process.exit(1));let l=e.includes("theme"),d=e.filter(o=>o!=="theme"),p=new Set,f=new Set,g=o=>{if(p.has(o))return;let s=v.find(i=>i.name===o);s&&(s.dependencies.forEach(i=>g(i)),s.npmDependencies?.forEach(i=>f.add(i)),p.add(o));};d.forEach(g),l&&f.add("next-themes");let N=[...p].filter(o=>!d.includes(o));N.length&&n.log.info(`Required dependency: ${N.join(", ")}`);let m=t.typescript?"tsx":"jsx",h=join(r,t.paths.components);for(let o of p){let s=join(h,`${o}.${m}`);if(x(s)){let S=await n.confirm({message:`${o}.${m} exists. Overwrite?`,initialValue:false});if(n.isCancel(S)||!S){n.log.warn(`Skipped ${o}`);continue}}let i=R[o];i=i.replace(/@\/utils/g,`@/${t.paths.utils.replace(/^src\//,"")}`),b(s,i),n.log.success(`${o} \u2192 ${t.paths.components}/${o}.${m}`);}if(l){let o=join(h,`theme-provider.${m}`);if(x(o)){let i=await n.confirm({message:`theme-provider.${m} exists. Overwrite?`,initialValue:false});!n.isCancel(i)&&i?(b(o,R.theme),n.log.success(`theme-provider \u2192 ${t.paths.components}/theme-provider.${m}`)):n.log.warn("Skipped theme-provider");}else b(o,R.theme),n.log.success(`theme-provider \u2192 ${t.paths.components}/theme-provider.${m}`);if(t.paths.globals){let i=join(r,t.paths.globals);if(x(i)){let S=H(i);S.includes('[data-theme="dark"]')?n.log.warn("Theme tokens already exist in globals \u2014 skipped"):(b(i,S+k),n.log.success(`Theme tokens appended to ${u.cyan(t.paths.globals)}`));}else n.log.warn(`Globals file not found at ${t.paths.globals}`);}else n.log.warn('No globals path in drivn.config.json. Add "globals" to paths');let s=t.paths.components.replace(/^src\//,"@/");n.log.message(""),n.log.info(u.bold("Complete the setup:")),n.log.message(""),n.log.message(u.bold(`${u.cyan("1.")} Import ThemeProvider in your root layout:`)),n.log.message(u.cyan(` import { ThemeProvider } from "${s}/theme-provider"`)),n.log.message(""),n.log.message(u.bold(`${u.cyan("2.")} Add suppressHydrationWarning to <html>:`)),n.log.message(u.cyan(" <html suppressHydrationWarning>")),n.log.message(""),n.log.message(u.bold(`${u.cyan("3.")} Wrap your app with ThemeProvider:`)),n.log.message(u.cyan(" <ThemeProvider>")),n.log.message(u.cyan(" {children}")),n.log.message(u.cyan(" </ThemeProvider>")),n.log.message("");}if(p.has("calendar")&&t.paths.globals){let o=join(r,t.paths.globals);if(x(o)){let s=H(o);s.includes(".rdp-root")?n.log.warn("Calendar tokens already exist in globals \u2014 skipped"):(b(o,s+Me),n.log.success(`Calendar tokens appended to ${u.cyan(t.paths.globals)}`));}}if(f.size){let o=E(r),s=[...f],i=n.spinner();i.start("Installing packages");try{execSync(w(o,s),{cwd:r,stdio:"ignore"}),i.stop("Packages installed");}catch{i.stop("Failed to install packages"),n.log.warn(`Run manually: ${w(o,s)}`);}}n.outro("Done.");}var j=`# Drivn Component Conventions
3807
4541
 
3808
4542
  ## Core Philosophy
3809
4543
  - **Zero runtime UI deps** \u2014 No Radix, no cva, no external UI primitives. Pure React + Tailwind.
@@ -3916,13 +4650,13 @@ import { cn } from '@/utils/cn'
3916
4650
  - Components declare internal deps (other Drivn components)
3917
4651
  - Some components need npm packages (react-day-picker, cmdk, embla-carousel-react, sonner)
3918
4652
  - The CLI resolves and installs all dependencies automatically
3919
- `;var L={version:"1.9.0"};function O(e){return v.find(r=>r.name===e)}function Ke(e){let r=new Set,t=a=>{if(r.has(a))return;let l=O(a);l&&(l.dependencies.forEach(d=>t(d)),r.add(a));};return t(e),r.delete(e),[...r]}async function Ie(){let e=new McpServer({name:"drivn",version:L.version});e.tool("list_components","List all available Drivn UI components with descriptions",{},async()=>({content:[{type:"text",text:JSON.stringify(v.map(t=>({name:t.name,description:t.description})),null,2)}]})),e.tool("get_component","Get the full source code and metadata for a Drivn component",{name:z$1.string().describe('Component name (e.g. "button", "dialog")')},async({name:t})=>{let a=O(t);if(!a)return {content:[{type:"text",text:`Component "${t}" not found. Use list_components to see available components.`}],isError:true};let l=w[t];return {content:[{type:"text",text:JSON.stringify({name:a.name,description:a.description,dependencies:a.dependencies,npmDependencies:a.npmDependencies,source:l},null,2)}]}}),e.tool("get_component_metadata","Get metadata only (no source code) for a Drivn component \u2014 useful for planning",{name:z$1.string().describe("Component name")},async({name:t})=>{let a=O(t);if(!a)return {content:[{type:"text",text:`Component "${t}" not found. Use list_components to see available components.`}],isError:true};let l=Ke(t);return {content:[{type:"text",text:JSON.stringify({name:a.name,description:a.description,dependencies:a.dependencies,npmDependencies:a.npmDependencies,allResolvedDependencies:l},null,2)}]}}),e.tool("search_components","Search Drivn components by name or description",{query:z$1.string().describe("Search query")},async({query:t})=>{let a=t.toLowerCase(),l=v.filter(d=>d.name.includes(a)||d.description.toLowerCase().includes(a));return {content:[{type:"text",text:l.length?JSON.stringify(l.map(d=>({name:d.name,description:d.description})),null,2):`No components matching "${t}".`}]}}),e.tool("get_installation_instructions","Get step-by-step installation instructions for one or more components",{components:z$1.array(z$1.string()).describe("Component names to install"),packageManager:z$1.enum(["npm","pnpm"]).optional().describe("Package manager (default: npm)")},async({components:t,packageManager:a})=>{let l=a??"npm",d=new Set,p=new Set,f=[],g=o=>{if(d.has(o))return;let s=O(o);if(!s){f.push(o);return}s.dependencies.forEach(i=>g(i)),s.npmDependencies.forEach(i=>p.add(i)),d.add(o);};if(t.forEach(g),f.length)return {content:[{type:"text",text:`Unknown components: ${f.join(", ")}. Use list_components to see available components.`}],isError:true};let N=l==="pnpm"?"pnpm dlx":"npx",u=l==="pnpm"?"pnpm add":"npm install",h=[];return h.push(`# Install components via CLI
4653
+ `;var L={version:"1.11.0"};function O(e){return v.find(r=>r.name===e)}function Je(e){let r=new Set,t=a=>{if(r.has(a))return;let l=O(a);l&&(l.dependencies.forEach(d=>t(d)),r.add(a));};return t(e),r.delete(e),[...r]}async function Be(){let e=new McpServer({name:"drivn",version:L.version});e.tool("list_components","List all available Drivn UI components with descriptions",{},async()=>({content:[{type:"text",text:JSON.stringify(v.map(t=>({name:t.name,description:t.description})),null,2)}]})),e.tool("get_component","Get the full source code and metadata for a Drivn component",{name:z$1.string().describe('Component name (e.g. "button", "dialog")')},async({name:t})=>{let a=O(t);if(!a)return {content:[{type:"text",text:`Component "${t}" not found. Use list_components to see available components.`}],isError:true};let l=R[t];return {content:[{type:"text",text:JSON.stringify({name:a.name,description:a.description,dependencies:a.dependencies,npmDependencies:a.npmDependencies,source:l},null,2)}]}}),e.tool("get_component_metadata","Get metadata only (no source code) for a Drivn component \u2014 useful for planning",{name:z$1.string().describe("Component name")},async({name:t})=>{let a=O(t);if(!a)return {content:[{type:"text",text:`Component "${t}" not found. Use list_components to see available components.`}],isError:true};let l=Je(t);return {content:[{type:"text",text:JSON.stringify({name:a.name,description:a.description,dependencies:a.dependencies,npmDependencies:a.npmDependencies,allResolvedDependencies:l},null,2)}]}}),e.tool("search_components","Search Drivn components by name or description",{query:z$1.string().describe("Search query")},async({query:t})=>{let a=t.toLowerCase(),l=v.filter(d=>d.name.includes(a)||d.description.toLowerCase().includes(a));return {content:[{type:"text",text:l.length?JSON.stringify(l.map(d=>({name:d.name,description:d.description})),null,2):`No components matching "${t}".`}]}}),e.tool("get_installation_instructions","Get step-by-step installation instructions for one or more components",{components:z$1.array(z$1.string()).describe("Component names to install"),packageManager:z$1.enum(["npm","pnpm"]).optional().describe("Package manager (default: npm)")},async({components:t,packageManager:a})=>{let l=a??"npm",d=new Set,p=new Set,f=[],g=o=>{if(d.has(o))return;let s=O(o);if(!s){f.push(o);return}s.dependencies.forEach(i=>g(i)),s.npmDependencies.forEach(i=>p.add(i)),d.add(o);};if(t.forEach(g),f.length)return {content:[{type:"text",text:`Unknown components: ${f.join(", ")}. Use list_components to see available components.`}],isError:true};let N=l==="pnpm"?"pnpm dlx":"npx",m=l==="pnpm"?"pnpm add":"npm install",h=[];return h.push(`# Install components via CLI
3920
4654
  ${N} drivn@latest add ${[...d].join(" ")}`),p.size&&h.push(`# Install required npm dependencies
3921
- ${u} ${[...p].join(" ")}`),h.push(`# Components will be installed to your configured components directory
4655
+ ${m} ${[...p].join(" ")}`),h.push(`# Components will be installed to your configured components directory
3922
4656
  # (default: src/components/ui/)`),{content:[{type:"text",text:JSON.stringify({componentsToInstall:[...d],npmDependencies:[...p],steps:h},null,2)}]}}),e.tool("get_design_tokens","Get the Drivn CSS design tokens (base globals and theme tokens)",{},async()=>({content:[{type:"text",text:`/* === Base Globals === */
3923
4657
  ${C}
3924
4658
 
3925
4659
  /* === Theme Tokens === */
3926
4660
  ${k}`}]})),e.tool("get_drivn_rules","Get Drivn coding conventions and component patterns",{},async()=>({content:[{type:"text",text:j}]})),e.resource("drivn-rules","drivn://rules",{description:"Drivn coding conventions and component patterns"},async()=>({contents:[{uri:"drivn://rules",mimeType:"text/markdown",text:j}]})),e.resource("drivn-design-tokens","drivn://design-tokens",{description:"Drivn CSS globals and theme tokens"},async()=>({contents:[{uri:"drivn://design-tokens",mimeType:"text/css",text:`${C}
3927
4661
 
3928
- ${k}`}]}));for(let t of v){let a=`drivn://components/${t.name}`;e.resource(`drivn-component-${t.name}`,a,{description:t.description},async()=>({contents:[{uri:a,mimeType:"text/plain",text:w[t.name]}]}));}let r=new StdioServerTransport;await e.connect(r);}var E=new Command;E.name("drivn").description("Drivn \u2014 Modern UI components").version(L.version);E.command("create").description("Initialize Drivn in your project").action(Y);E.command("add [components...]").description("Add components to your project").action(Ee);E.command("mcp").description("Start the Drivn MCP server").action(Ie);E.parse();
4662
+ ${k}`}]}));for(let t of v){let a=`drivn://components/${t.name}`;e.resource(`drivn-component-${t.name}`,a,{description:t.description},async()=>({contents:[{uri:a,mimeType:"text/plain",text:R[t.name]}]}));}let r=new StdioServerTransport;await e.connect(r);}var I=new Command;I.name("drivn").description("Drivn \u2014 Modern UI components").version(L.version);I.command("create").description("Initialize Drivn in your project").action(Y);I.command("add [components...]").description("Add components to your project").action(Ae);I.command("mcp").description("Start the Drivn MCP server").action(Be);I.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "drivn",
3
- "version": "1.9.0",
3
+ "version": "1.11.0",
4
4
  "description": "Modern UI components for React — add beautiful, accessible components to your project",
5
5
  "license": "MIT",
6
6
  "type": "module",