create-lexy 0.1.1 → 0.2.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 (54) hide show
  1. package/assets/r/accordion.json +24 -0
  2. package/assets/r/app-accordion.json +24 -0
  3. package/assets/r/app-dialog.json +25 -0
  4. package/assets/r/app-header-bar.json +28 -0
  5. package/assets/r/app-sidebar.json +27 -0
  6. package/assets/r/avatar.json +24 -0
  7. package/assets/r/badge.json +24 -0
  8. package/assets/r/breadcrumb.json +24 -0
  9. package/assets/r/button-group.json +27 -0
  10. package/assets/r/calendar.json +27 -0
  11. package/assets/r/card.json +24 -0
  12. package/assets/r/chart.json +24 -0
  13. package/assets/r/checkbox.json +24 -0
  14. package/assets/r/combobox.json +26 -0
  15. package/assets/r/command.json +24 -0
  16. package/assets/r/counter-badge.json +24 -0
  17. package/assets/r/date-picker.json +29 -0
  18. package/assets/r/dialog.json +24 -0
  19. package/assets/r/dropdown-menu.json +24 -0
  20. package/assets/r/empty.json +24 -0
  21. package/assets/r/feature-card.json +22 -0
  22. package/assets/r/header-bar.json +26 -0
  23. package/assets/r/input.json +22 -0
  24. package/assets/r/label.json +25 -0
  25. package/assets/r/logo.json +130 -0
  26. package/assets/r/menubar.json +24 -0
  27. package/assets/r/navigation-menu.json +25 -0
  28. package/assets/r/pagination.json +27 -0
  29. package/assets/r/popover.json +24 -0
  30. package/assets/r/profile-card.json +25 -0
  31. package/assets/r/progress.json +24 -0
  32. package/assets/r/radio-group.json +24 -0
  33. package/assets/r/registry.json +1168 -0
  34. package/assets/r/scroll-area.json +24 -0
  35. package/assets/r/searchbox.json +22 -0
  36. package/assets/r/select.json +24 -0
  37. package/assets/r/separator.json +24 -0
  38. package/assets/r/sheet.json +25 -0
  39. package/assets/r/sidebar.json +33 -0
  40. package/assets/r/skeleton.json +24 -0
  41. package/assets/r/slider.json +24 -0
  42. package/assets/r/snippet.json +24 -0
  43. package/assets/r/spinner.json +25 -0
  44. package/assets/r/status-dot.json +24 -0
  45. package/assets/r/switch.json +24 -0
  46. package/assets/r/table.json +24 -0
  47. package/assets/r/tabs.json +24 -0
  48. package/assets/r/tag.json +24 -0
  49. package/assets/r/textarea.json +22 -0
  50. package/assets/r/tooltip.json +24 -0
  51. package/assets/r/tree.json +22 -0
  52. package/assets/theme/lexy-theme.css +3 -0
  53. package/dist/index.js +36 -36
  54. package/package.json +1 -1
@@ -0,0 +1,26 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "combobox",
4
+ "type": "registry:ui",
5
+ "title": "Combobox",
6
+ "description": "Campo de entrada con lista desplegable filtrable. Para búsqueda + selección. El componente vive en tu proyecto: instálalo con `create-lexy add combobox` y edítalo con libertad.",
7
+ "dependencies": [],
8
+ "registryDependencies": [
9
+ "button",
10
+ "command",
11
+ "popover"
12
+ ],
13
+ "files": [
14
+ {
15
+ "path": "components/base/Combobox.tsx",
16
+ "type": "registry:ui",
17
+ "content": "import * as React from \"react\";\n\nimport { cn } from \"@/lib/utils/cn\";\nimport { Button } from \"./Button\";\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from \"./Command\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"./Popover\";\n\nexport interface ComboboxOption {\n value: string;\n label: string;\n}\n\n/**\n * Wrapper ergonómico sobre la receta compuesta Popover + Command. Si necesitas\n * variar la estructura (grupos, ítems con iconos, multi-selección, creación de\n * opciones), compón las piezas directamente — ver \"Receta compuesta\" en\n * Combobox.md.\n */\nexport interface ComboboxProps\n extends Omit<\n React.ComponentPropsWithoutRef<typeof Button>,\n \"value\" | \"defaultValue\" | \"onChange\"\n > {\n options: ComboboxOption[];\n value?: string;\n defaultValue?: string;\n onValueChange?: (value: string) => void;\n placeholder?: string;\n searchPlaceholder?: string;\n emptyMessage?: string;\n}\n\nconst Combobox = React.forwardRef<HTMLButtonElement, ComboboxProps>(\n function Combobox(\n {\n options,\n value,\n defaultValue,\n onValueChange,\n placeholder = \"Seleccionar…\",\n searchPlaceholder = \"Buscar…\",\n emptyMessage = \"Sin resultados\",\n className,\n ...props\n },\n ref,\n ) {\n const [open, setOpen] = React.useState(false);\n const [internal, setInternal] = React.useState(defaultValue ?? \"\");\n const current = value ?? internal;\n const selected = options.find((option) => option.value === current);\n\n const handleSelect = (next: string) => {\n const resolved = next === current ? \"\" : next;\n setInternal(resolved);\n onValueChange?.(resolved);\n setOpen(false);\n };\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button\n ref={ref}\n variant='outline'\n role='combobox'\n aria-expanded={open}\n className={cn(\"w-full justify-between font-normal\", className)}\n {...props}>\n {selected ? (\n selected.label\n ) : (\n <span className='text-muted-foreground'>{placeholder}</span>\n )}\n <svg\n className='ml-2 h-4 w-4 shrink-0 opacity-50'\n viewBox='0 0 24 24'\n fill='none'\n stroke='currentColor'\n strokeWidth='2'\n strokeLinecap='round'\n strokeLinejoin='round'>\n <path d='m7 15 5 5 5-5M7 9l5-5 5 5' />\n </svg>\n </Button>\n </PopoverTrigger>\n <PopoverContent\n align='start'\n className='w-[var(--radix-popover-trigger-width)] p-0'>\n <Command>\n <CommandInput placeholder={searchPlaceholder} />\n <CommandList>\n <CommandEmpty>{emptyMessage}</CommandEmpty>\n <CommandGroup>\n {options.map((option) => (\n <CommandItem\n key={option.value}\n value={option.label}\n onSelect={() => handleSelect(option.value)}>\n <svg\n className={cn(\n \"mr-2 h-4 w-4\",\n current === option.value ? \"opacity-100\" : \"opacity-0\",\n )}\n viewBox='0 0 24 24'\n fill='none'\n stroke='currentColor'\n strokeWidth='2'\n strokeLinecap='round'\n strokeLinejoin='round'>\n <path d='M20 6 9 17l-5-5' />\n </svg>\n {option.label}\n </CommandItem>\n ))}\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n );\n },\n);\nCombobox.displayName = \"Combobox\";\n\nexport { Combobox };\n"
18
+ },
19
+ {
20
+ "path": "components/base/Combobox.md",
21
+ "type": "registry:file",
22
+ "target": "components/base/Combobox.md",
23
+ "content": "# Combobox — guía de uso\n\nCampo de entrada con lista desplegable filtrable. Para búsqueda + selección. El componente vive en tu proyecto: instálalo con `create-lexy add combobox` y edítalo con libertad.\n\n## Import\n\n```tsx\nimport { Combobox } from \"@/components/base/Combobox\";\n```\n\n## Uso básico\n\n```tsx\n<Combobox />\n```\n\n## Props\n\nConsulta la story en Storybook para ver las props disponibles.\n\n## Receta compuesta\n\n`Combobox` es un wrapper delgado sobre `Popover` + `Command`. Sus props extra (`disabled`, `id`, `aria-*`…) pasan al botón trigger y el `ref` apunta a ese botón. Si necesitas variar la estructura (grupos, ítems con icono o descripción, multi-selección, crear opciones al vuelo), compón las piezas directamente:\n\n```tsx\nimport { Button } from \"@/components/base/Button\";\nimport { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from \"@/components/base/Command\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"@/components/base/Popover\";\n\n<Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button variant=\"outline\" role=\"combobox\" aria-expanded={open}>…</Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-[var(--radix-popover-trigger-width)] p-0\" align=\"start\">\n <Command>\n <CommandInput placeholder=\"Buscar…\" />\n <CommandList>\n <CommandEmpty>Sin resultados</CommandEmpty>\n <CommandGroup heading=\"Sugeridos\">{/* CommandItem con iconos, etc. */}</CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n</Popover>\n```\n\n## Reglas\n\n- Usa Combobox según el propósito descrito.\n- No abuses de este componente en contexts donde no aplica.\n\n## Para IA\n\n1. Identifica el contexto de uso del componente.\n2. Importa Combobox desde el path correcto.\n3. Configura las props según la necesidad.\n4. Verifica que el componente se integre correctamente en el layout.\n5. Solo usa variantes documentadas.\n"
24
+ }
25
+ ]
26
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "command",
4
+ "type": "registry:ui",
5
+ "title": "Command",
6
+ "description": "Paleta de comandos. Para búsqueda y acciones rápidas. El componente vive en tu proyecto: instálalo con `create-lexy add command` y edítalo con libertad.",
7
+ "dependencies": [
8
+ "cmdk"
9
+ ],
10
+ "registryDependencies": [],
11
+ "files": [
12
+ {
13
+ "path": "components/base/Command.tsx",
14
+ "type": "registry:ui",
15
+ "content": "import * as React from \"react\";\nimport { Command as CommandPrimitive } from \"cmdk\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nconst Command = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive>\n>(({ className, ...props }, ref) => (\n <CommandPrimitive\n ref={ref}\n className={cn(\n \"flex h-full w-full flex-col overflow-hidden rounded bg-popover text-popover-foreground\",\n className,\n )}\n {...props}\n />\n));\nCommand.displayName = CommandPrimitive.displayName;\n\nconst CommandInput = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive.Input>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>\n>(({ className, ...props }, ref) => (\n <div className='flex items-center border-b border-border px-3' cmdk-input-wrapper=''>\n <svg\n className='mr-2 h-4 w-4 shrink-0 opacity-50'\n viewBox='0 0 24 24'\n fill='none'\n stroke='currentColor'\n strokeWidth='2'\n strokeLinecap='round'\n strokeLinejoin='round'>\n <circle cx='11' cy='11' r='8' />\n <path d='m21 21-4.3-4.3' />\n </svg>\n <CommandPrimitive.Input\n ref={ref}\n className={cn(\n \"flex h-10 w-full rounded bg-transparent py-3 text-sm text-foreground outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50\",\n className,\n )}\n {...props}\n />\n </div>\n));\nCommandInput.displayName = CommandPrimitive.Input.displayName;\n\nconst CommandList = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive.List>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>\n>(({ className, ...props }, ref) => (\n <CommandPrimitive.List\n ref={ref}\n className={cn(\"max-h-75 overflow-y-auto overflow-x-hidden\", className)}\n {...props}\n />\n));\nCommandList.displayName = CommandPrimitive.List.displayName;\n\nconst CommandEmpty = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive.Empty>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>\n>((props, ref) => (\n <CommandPrimitive.Empty\n ref={ref}\n className='py-6 text-center text-sm text-muted-foreground'\n {...props}\n />\n));\nCommandEmpty.displayName = CommandPrimitive.Empty.displayName;\n\nconst CommandGroup = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive.Group>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>\n>(({ className, ...props }, ref) => (\n <CommandPrimitive.Group\n ref={ref}\n className={cn(\n \"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground\",\n className,\n )}\n {...props}\n />\n));\nCommandGroup.displayName = CommandPrimitive.Group.displayName;\n\nconst CommandSeparator = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive.Separator>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <CommandPrimitive.Separator\n ref={ref}\n className={cn(\"-mx-1 h-px bg-border\", className)}\n {...props}\n />\n));\nCommandSeparator.displayName = CommandPrimitive.Separator.displayName;\n\nconst CommandItem = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>\n>(({ className, ...props }, ref) => (\n <CommandPrimitive.Item\n ref={ref}\n className={cn(\n \"relative flex cursor-pointer select-none items-center gap-2 rounded px-2 py-1.5 text-sm text-foreground outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-primary/15 data-[selected=true]:text-primary data-[disabled=true]:opacity-50\",\n className,\n )}\n {...props}\n />\n));\nCommandItem.displayName = CommandPrimitive.Item.displayName;\n\nconst CommandShortcut = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLSpanElement>) => (\n <span\n className={cn(\n \"ml-auto text-xs tracking-widest text-muted-foreground\",\n className,\n )}\n {...props}\n />\n);\nCommandShortcut.displayName = \"CommandShortcut\";\n\nexport {\n Command,\n CommandInput,\n CommandList,\n CommandEmpty,\n CommandGroup,\n CommandItem,\n CommandShortcut,\n CommandSeparator,\n};\n"
16
+ },
17
+ {
18
+ "path": "components/base/Command.md",
19
+ "type": "registry:file",
20
+ "target": "components/base/Command.md",
21
+ "content": "# Command — guía de uso\n\nPaleta de comandos. Para búsqueda y acciones rápidas. El componente vive en tu proyecto: instálalo con `create-lexy add command` y edítalo con libertad.\n\n## Import\n\n```tsx\nimport { Command } from \"@/components/base/Command\";\n```\n\n## Uso básico\n\n```tsx\n<Command />\n```\n\n## Props\n\nConsulta la story en Storybook para ver las props disponibles.\n\n## Reglas\n\n- Usa Command según el propósito descrito.\n- No abuses de este componente en contexts donde no aplica.\n\n## Para IA\n\n1. Identifica el contexto de uso del componente.\n2. Importa Command desde el path correcto.\n3. Configura las props según la necesidad.\n4. Verifica que el componente se integre correctamente en el layout.\n5. Solo usa variantes documentadas.\n"
22
+ }
23
+ ]
24
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "counter-badge",
4
+ "type": "registry:ui",
5
+ "title": "CounterBadge",
6
+ "description": "Badge con contador numérico. Para notificaciones. El componente vive en tu proyecto: instálalo con `create-lexy add counter-badge` y edítalo con libertad.",
7
+ "dependencies": [
8
+ "class-variance-authority"
9
+ ],
10
+ "registryDependencies": [],
11
+ "files": [
12
+ {
13
+ "path": "components/base/CounterBadge.tsx",
14
+ "type": "registry:ui",
15
+ "content": "import * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nfunction formatCount(children: React.ReactNode, max?: number): React.ReactNode {\n if (max == null) return children;\n const n =\n typeof children === \"number\"\n ? children\n : typeof children === \"string\" && /^\\d+$/.test(children.trim())\n ? Number.parseInt(children, 10)\n : null;\n if (n == null || n <= max) return children;\n return `${max}+`;\n}\n\nconst counterBadgeVariants = cva(\n \"inline-flex select-none items-center justify-center rounded-full bg-primary font-semibold tabular-nums text-primary-foreground\",\n {\n variants: {\n size: {\n sm: \"h-4 min-w-4 px-1 text-xs leading-none\",\n md: \"h-5 min-w-5 px-1.5 text-xs leading-none\",\n lg: \"h-6 min-w-6 px-1.5 text-xs/[1]\",\n },\n },\n defaultVariants: { size: \"md\" },\n },\n);\n\nexport interface CounterBadgeProps\n extends React.HTMLAttributes<HTMLSpanElement>,\n VariantProps<typeof counterBadgeVariants> {\n /** Tope numérico (ej. 99 → \"99+\"). */\n max?: number;\n}\n\nconst CounterBadge = React.forwardRef<HTMLSpanElement, CounterBadgeProps>(\n ({ size, max, className, children, ...props }, ref) => (\n <span\n ref={ref}\n className={cn(counterBadgeVariants({ size }), className)}\n {...props}>\n {formatCount(children, max)}\n </span>\n ),\n);\nCounterBadge.displayName = \"CounterBadge\";\n\nexport { CounterBadge, counterBadgeVariants };\n"
16
+ },
17
+ {
18
+ "path": "components/base/CounterBadge.md",
19
+ "type": "registry:file",
20
+ "target": "components/base/CounterBadge.md",
21
+ "content": "# CounterBadge — guía de uso\n\nBadge con contador numérico. Para notificaciones. El componente vive en tu proyecto: instálalo con `create-lexy add counter-badge` y edítalo con libertad.\n\n## Import\n\n```tsx\nimport { CounterBadge } from \"@/components/base/CounterBadge\";\n```\n\n## Uso básico\n\n```tsx\n<CounterBadge />\n```\n\n## Props\n\nConsulta la story en Storybook para ver las props disponibles.\n\n## Reglas\n\n- Usa CounterBadge según el propósito descrito.\n- No abuses de este componente en contexts donde no aplica.\n\n## Para IA\n\n1. Identifica el contexto de uso del componente.\n2. Importa CounterBadge desde el path correcto.\n3. Configura las props según la necesidad.\n4. Verifica que el componente se integre correctamente en el layout.\n5. Solo usa variantes documentadas.\n"
22
+ }
23
+ ]
24
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "date-picker",
4
+ "type": "registry:ui",
5
+ "title": "DatePicker",
6
+ "description": "Selector de fecha en un campo: un `Button` que abre un `Popover` con un",
7
+ "dependencies": [
8
+ "lucide-react",
9
+ "react-day-picker@^9.8.0"
10
+ ],
11
+ "registryDependencies": [
12
+ "button",
13
+ "calendar",
14
+ "popover"
15
+ ],
16
+ "files": [
17
+ {
18
+ "path": "components/base/DatePicker.tsx",
19
+ "type": "registry:ui",
20
+ "content": "import * as React from \"react\";\nimport { CalendarIcon } from \"lucide-react\";\nimport { es } from \"react-day-picker/locale\";\nimport type { Locale } from \"react-day-picker\";\n\nimport { cn } from \"@/lib/utils/cn\";\nimport { Button } from \"./Button\";\nimport { Calendar } from \"./Calendar\";\nimport { Popover, PopoverTrigger, PopoverContent } from \"./Popover\";\n\nfunction formatDate(date: Date, localeCode?: string) {\n return date.toLocaleDateString(localeCode, {\n day: \"numeric\",\n month: \"long\",\n year: \"numeric\",\n });\n}\n\n/**\n * Wrapper ergonómico sobre la receta compuesta Popover + Button + Calendar.\n * Si necesitas variar la estructura (rango, dos meses, footer, otro trigger),\n * compón las piezas directamente — ver \"Receta compuesta\" en DatePicker.md.\n */\nexport interface DatePickerProps\n extends Omit<\n React.ComponentPropsWithoutRef<typeof Button>,\n \"value\" | \"defaultValue\" | \"onChange\"\n > {\n /** Fecha seleccionada (modo controlado: pásala junto a `onChange`). */\n value?: Date;\n /** Fecha inicial en modo no controlado. */\n defaultValue?: Date;\n /** Callback de selección (activa el modo controlado). */\n onChange?: (date: Date | undefined) => void;\n placeholder?: string;\n captionLayout?: React.ComponentProps<typeof Calendar>[\"captionLayout\"];\n locale?: Partial<Locale>;\n align?: React.ComponentProps<typeof PopoverContent>[\"align\"];\n}\n\nconst DatePicker = React.forwardRef<HTMLButtonElement, DatePickerProps>(\n function DatePicker(\n {\n value,\n defaultValue,\n onChange,\n placeholder = \"Selecciona una fecha\",\n captionLayout = \"label\",\n locale = es,\n align = \"start\",\n className,\n ...props\n },\n ref,\n ) {\n const [open, setOpen] = React.useState(false);\n const [internal, setInternal] = React.useState<Date | undefined>(\n defaultValue,\n );\n\n // Controlado si se provee `onChange`; si no, mantenemos estado interno.\n const selected = onChange ? value : internal;\n\n const handleSelect = (date: Date | undefined) => {\n if (onChange) onChange(date);\n else setInternal(date);\n setOpen(false);\n };\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button\n ref={ref}\n type=\"button\"\n variant=\"outline\"\n data-empty={!selected}\n className={cn(\n \"w-65 justify-start text-left font-normal data-[empty=true]:text-muted-foreground\",\n className,\n )}\n {...props}\n >\n <CalendarIcon />\n {selected ? formatDate(selected, locale?.code) : placeholder}\n </Button>\n </PopoverTrigger>\n {/* El panel toma el ancho del trigger (igual que Combobox); el calendario\n (248px natural) se centra dentro. */}\n <PopoverContent\n className=\"flex w-[var(--radix-popover-trigger-width)] justify-center p-0\"\n align={align}\n >\n <Calendar\n mode=\"single\"\n selected={selected}\n onSelect={handleSelect}\n captionLayout={captionLayout}\n locale={locale}\n autoFocus\n />\n </PopoverContent>\n </Popover>\n );\n },\n);\nDatePicker.displayName = \"DatePicker\";\n\nexport { DatePicker };\n"
21
+ },
22
+ {
23
+ "path": "components/base/DatePicker.md",
24
+ "type": "registry:file",
25
+ "target": "components/base/DatePicker.md",
26
+ "content": "# DatePicker — guía de uso\n\nSelector de fecha en un campo: un `Button` que abre un `Popover` con un\n`Calendar` adentro. Es la composición estándar para elegir una fecha en\nformularios. Muestra la fecha elegida formateada (en español por defecto). El componente vive en tu proyecto: instálalo con `create-lexy add date-picker` y edítalo con libertad.\n\n## Import\n\n```tsx\nimport { DatePicker } from \"@/components/base/DatePicker\";\n```\n\n## Uso básico (no controlado)\n\n```tsx\n<DatePicker />\n```\n\n## Controlado\n\n```tsx\nconst [date, setDate] = React.useState<Date | undefined>();\n\n<DatePicker value={date} onChange={setDate} />;\n```\n\n## Fecha de nacimiento (dropdowns de mes/año)\n\n```tsx\n<DatePicker captionLayout=\"dropdown\" placeholder=\"Fecha de nacimiento\" />\n```\n\n## Props\n\n| Prop | Tipo | Default | Descripción |\n|---|---|---|---|\n| `value` | `Date` | — | Fecha seleccionada (modo controlado, junto a `onChange`). |\n| `defaultValue` | `Date` | — | Fecha inicial en modo no controlado. |\n| `onChange` | `(date?: Date) => void` | — | Callback de selección. Activa el modo controlado. |\n| `placeholder` | `string` | `\"Selecciona una fecha\"` | Texto cuando no hay fecha. |\n| `disabled` | `boolean` | `false` | Deshabilita el disparador. |\n| `captionLayout` | `\"label\" \\| \"dropdown\" \\| …` | `\"label\"` | Encabezado del calendario. |\n| `locale` | `Locale` | `es` | Localización (de `react-day-picker/locale`). |\n| `align` | `\"start\" \\| \"center\" \\| \"end\"` | `\"start\"` | Alineación del popover. |\n| `className` | `string` | — | Clases del botón disparador (p. ej. ancho). |\n\n## Receta compuesta\n\n`DatePicker` es un wrapper delgado sobre `Popover` + `Button` + `Calendar`. Sus props extra (`variant`, `disabled`, `id`, `aria-*`…) pasan al botón trigger y el `ref` apunta a ese botón. Si necesitas variar la estructura (rango, dos meses, footer con acciones, otro trigger), compón las piezas directamente:\n\n```tsx\nimport { Button } from \"@/components/base/Button\";\nimport { Calendar } from \"@/components/base/Calendar\";\nimport { Popover, PopoverTrigger, PopoverContent } from \"@/components/base/Popover\";\n\n<Popover>\n <PopoverTrigger asChild>\n <Button variant=\"outline\">{fecha ? formatear(fecha) : \"Selecciona una fecha\"}</Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-auto p-0\" align=\"start\">\n <Calendar mode=\"range\" numberOfMonths={2} selected={rango} onSelect={setRango} />\n </PopoverContent>\n</Popover>\n```\n\n## Reglas\n\n- Para una sola fecha en un formulario, usa `DatePicker`; para elegir un período usa `Calendar` con `mode=\"range\"` dentro de un `Popover`.\n- En modo controlado pasa `value` **y** `onChange`; sin `onChange` el componente maneja su propio estado.\n- El popover se cierra al seleccionar una fecha.\n- Ajusta el ancho del campo con `className` (por defecto `w-65`).\n\n## Cuándo NO usar\n\n- **Elegir un rango** → `Calendar` con `mode=\"range\"` (date range picker).\n- **Mostrar un calendario siempre visible** → `Calendar` directo, sin `Popover`.\n- **Solo hora** → un input de tiempo.\n\n## Para IA\n\n1. Para una fecha en formulario, usa `DatePicker` (no reconstruyas Popover+Calendar a mano salvo que necesites rango).\n2. Modo controlado: `value` + `onChange` con estado propio; no controlado: solo `<DatePicker />`.\n3. Usa `captionLayout=\"dropdown\"` para fechas lejanas (nacimiento, vencimientos).\n4. Para rango, compón `Popover` + `Button` + `Calendar mode=\"range\"`.\n"
27
+ }
28
+ ]
29
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "dialog",
4
+ "type": "registry:ui",
5
+ "title": "Dialog",
6
+ "description": "Diálogo modal primitivo (Radix). **Para la mayoría de los casos usa `AppDialog`**, que ya arma la composición Material 3 (icono, título, descripción, acciones). Usa `Dialog` directo solo para layouts modales a medida sin esa estructura. El componente vive en tu proyecto: instálalo con `create-lexy add dialog` y edítalo con libertad.",
7
+ "dependencies": [
8
+ "@radix-ui/react-dialog"
9
+ ],
10
+ "registryDependencies": [],
11
+ "files": [
12
+ {
13
+ "path": "components/base/Dialog.tsx",
14
+ "type": "registry:ui",
15
+ "content": "import * as React from \"react\";\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nconst Dialog = DialogPrimitive.Root;\nconst DialogTrigger = DialogPrimitive.Trigger;\nconst DialogPortal = DialogPrimitive.Portal;\nconst DialogClose = DialogPrimitive.Close;\n\nconst DialogOverlay = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Overlay>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Overlay\n ref={ref}\n className={cn(\n \"fixed inset-0 z-50 bg-black/50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n className,\n )}\n {...props}\n />\n));\nDialogOverlay.displayName = DialogPrimitive.Overlay.displayName;\n\nconst DialogContent = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>\n>(({ className, children, ...props }, ref) => (\n <DialogPortal>\n <DialogOverlay />\n <DialogPrimitive.Content\n ref={ref}\n className={cn(\n \"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 sm:rounded-lg\",\n className,\n )}\n {...props}>\n {children}\n {/* Sin X de cierre: se cierra con Escape, click en el overlay o una\n acción explícita (DialogClose / botón Cancelar de AppDialog). */}\n </DialogPrimitive.Content>\n </DialogPortal>\n));\nDialogContent.displayName = DialogPrimitive.Content.displayName;\n\nconst DialogHeader = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col space-y-2 text-center sm:text-left\",\n className,\n )}\n {...props}\n />\n);\nDialogHeader.displayName = \"DialogHeader\";\n\nconst DialogFooter = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\",\n className,\n )}\n {...props}\n />\n);\nDialogFooter.displayName = \"DialogFooter\";\n\nconst DialogTitle = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Title\n ref={ref}\n className={cn(\n \"text-lg font-semibold leading-none tracking-tight text-foreground\",\n className,\n )}\n {...props}\n />\n));\nDialogTitle.displayName = DialogPrimitive.Title.displayName;\n\nconst DialogDescription = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Description\n ref={ref}\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n));\nDialogDescription.displayName = DialogPrimitive.Description.displayName;\n\nexport {\n Dialog,\n DialogPortal,\n DialogOverlay,\n DialogClose,\n DialogTrigger,\n DialogContent,\n DialogHeader,\n DialogFooter,\n DialogTitle,\n DialogDescription,\n};\n"
16
+ },
17
+ {
18
+ "path": "components/base/Dialog.md",
19
+ "type": "registry:file",
20
+ "target": "components/base/Dialog.md",
21
+ "content": "# Dialog — guía de uso\n\nDiálogo modal primitivo (Radix). **Para la mayoría de los casos usa `AppDialog`**, que ya arma la composición Material 3 (icono, título, descripción, acciones). Usa `Dialog` directo solo para layouts modales a medida sin esa estructura. El componente vive en tu proyecto: instálalo con `create-lexy add dialog` y edítalo con libertad.\n\n## Import\n\n```tsx\nimport {\n Dialog, DialogTrigger, DialogContent, DialogHeader,\n DialogTitle, DialogDescription, DialogFooter, DialogClose,\n} from \"@/components/base/Dialog\";\n```\n\n## Uso básico\n\n```tsx\n<Dialog>\n <DialogTrigger asChild>\n <Button variant=\"outline\">Ver detalle</Button>\n </DialogTrigger>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>Detalle del documento</DialogTitle>\n <DialogDescription>Resumen generado automáticamente.</DialogDescription>\n </DialogHeader>\n {/* contenido a medida */}\n <DialogFooter>\n <DialogClose asChild><Button variant=\"ghost\">Cerrar</Button></DialogClose>\n </DialogFooter>\n </DialogContent>\n</Dialog>\n```\n\n## Exports\n\n`Dialog`, `DialogTrigger`, `DialogContent`, `DialogHeader`, `DialogFooter`, `DialogTitle`, `DialogDescription`, `DialogClose`, `DialogOverlay`, `DialogPortal`.\n\n## Reglas\n\n- Incluye **siempre** un `DialogTitle` (accesibilidad: nombra el diálogo). Si lo ocultas visualmente, mantenlo para lectores de pantalla.\n- Usa `DialogDescription` para el contexto; no dejes el cuerpo sin explicación.\n- Usa `DialogTrigger asChild` / `DialogClose asChild` sobre tus propios botones.\n- No anides diálogos; un modal a la vez.\n- El diálogo **no trae X de cierre**: se cierra con Escape, click en el overlay o un botón explícito con `DialogClose` (el patrón Lexy es nombrar la acción, p. ej. «Cancelar»).\n- Explica la consecuencia de las acciones (ver pauta de UX writing), sin \"¿estás seguro?\".\n\n## Cuándo NO usar\n\n- **Confirmación o formulario modal estándar** → `AppDialog` (menos código, patrón correcto).\n- **Panel lateral** (filtros, detalle largo) → `Sheet`.\n- **Ayuda breve anclada a un control** → `Popover`/`Tooltip`.\n- **Mensaje efímero de resultado** → `Toast`.\n\n## Para IA\n\n1. Pregúntate primero si `AppDialog` cubre el caso (confirmación/formulario): si sí, úsalo.\n2. Usa `Dialog` directo solo para un layout modal a medida.\n3. Garantiza `DialogTitle` (visible o sr-only) y descripción.\n4. Conecta trigger/close con `asChild` a tus botones.\n5. Redacta acciones por su consecuencia; usa `Sheet`/`Popover`/`Toast` si encajan mejor.\n"
22
+ }
23
+ ]
24
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "dropdown-menu",
4
+ "type": "registry:ui",
5
+ "title": "DropdownMenu",
6
+ "description": "Menú de **acciones** contextuales que se abre desde un disparador (Radix). Para agrupar acciones secundarias (\"⋯\", \"Opciones\") sin llenar la pantalla de botones. El componente vive en tu proyecto: instálalo con `create-lexy add dropdown-menu` y edítalo con libertad.",
7
+ "dependencies": [
8
+ "@radix-ui/react-dropdown-menu"
9
+ ],
10
+ "registryDependencies": [],
11
+ "files": [
12
+ {
13
+ "path": "components/base/DropdownMenu.tsx",
14
+ "type": "registry:ui",
15
+ "content": "import * as React from \"react\";\nimport * as DropdownMenuPrimitive from \"@radix-ui/react-dropdown-menu\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nconst DropdownMenu = DropdownMenuPrimitive.Root;\nconst DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;\nconst DropdownMenuGroup = DropdownMenuPrimitive.Group;\nconst DropdownMenuPortal = DropdownMenuPrimitive.Portal;\nconst DropdownMenuSub = DropdownMenuPrimitive.Sub;\nconst DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;\n\nconst ChevronRightIcon = () => (\n <svg\n className='ml-auto h-4 w-4'\n viewBox='0 0 24 24'\n fill='none'\n stroke='currentColor'\n strokeWidth='2'\n strokeLinecap='round'\n strokeLinejoin='round'>\n <path d='m9 18 6-6-6-6' />\n </svg>\n);\n\nconst CheckIcon = () => (\n <svg\n className='h-4 w-4'\n viewBox='0 0 24 24'\n fill='none'\n stroke='currentColor'\n strokeWidth='2'\n strokeLinecap='round'\n strokeLinejoin='round'>\n <path d='M20 6 9 17l-5-5' />\n </svg>\n);\n\nconst DotIcon = () => (\n <svg className='h-2 w-2 fill-current' viewBox='0 0 24 24'>\n <circle cx='12' cy='12' r='12' />\n </svg>\n);\n\nconst DropdownMenuSubTrigger = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {\n inset?: boolean;\n }\n>(({ className, inset, children, ...props }, ref) => (\n <DropdownMenuPrimitive.SubTrigger\n ref={ref}\n className={cn(\n \"flex cursor-pointer select-none items-center rounded px-2 py-1.5 text-sm outline-none focus:bg-primary/15 focus:text-primary data-[state=open]:bg-primary/15 data-[state=open]:text-primary\",\n inset && \"pl-8\",\n className,\n )}\n {...props}>\n {children}\n <ChevronRightIcon />\n </DropdownMenuPrimitive.SubTrigger>\n));\nDropdownMenuSubTrigger.displayName =\n DropdownMenuPrimitive.SubTrigger.displayName;\n\nconst DropdownMenuSubContent = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.SubContent\n ref={ref}\n className={cn(\n \"z-50 min-w-32 overflow-hidden rounded border border-border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95\",\n className,\n )}\n {...props}\n />\n));\nDropdownMenuSubContent.displayName =\n DropdownMenuPrimitive.SubContent.displayName;\n\nconst DropdownMenuContent = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>\n>(({ className, sideOffset = 4, ...props }, ref) => (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n ref={ref}\n sideOffset={sideOffset}\n className={cn(\n \"z-50 min-w-32 overflow-hidden rounded border border-border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95\",\n className,\n )}\n {...props}\n />\n </DropdownMenuPrimitive.Portal>\n));\nDropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;\n\nconst DropdownMenuItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {\n inset?: boolean;\n }\n>(({ className, inset, ...props }, ref) => (\n <DropdownMenuPrimitive.Item\n ref={ref}\n className={cn(\n \"relative flex cursor-pointer select-none items-center gap-2 rounded px-2 py-1.5 text-sm outline-none transition-colors focus:bg-primary/15 focus:text-primary data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n inset && \"pl-8\",\n className,\n )}\n {...props}\n />\n));\nDropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;\n\nconst DropdownMenuCheckboxItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>\n>(({ className, children, checked, ...props }, ref) => (\n <DropdownMenuPrimitive.CheckboxItem\n ref={ref}\n className={cn(\n \"relative flex cursor-pointer select-none items-center rounded py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-primary/15 focus:text-primary data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n className,\n )}\n checked={checked}\n {...props}>\n <span className='absolute left-2 flex h-3.5 w-3.5 items-center justify-center'>\n <DropdownMenuPrimitive.ItemIndicator>\n <CheckIcon />\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </DropdownMenuPrimitive.CheckboxItem>\n));\nDropdownMenuCheckboxItem.displayName =\n DropdownMenuPrimitive.CheckboxItem.displayName;\n\nconst DropdownMenuRadioItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>\n>(({ className, children, ...props }, ref) => (\n <DropdownMenuPrimitive.RadioItem\n ref={ref}\n className={cn(\n \"relative flex cursor-pointer select-none items-center rounded py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-primary/15 focus:text-primary data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n className,\n )}\n {...props}>\n <span className='absolute left-2 flex h-3.5 w-3.5 items-center justify-center'>\n <DropdownMenuPrimitive.ItemIndicator>\n <DotIcon />\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </DropdownMenuPrimitive.RadioItem>\n));\nDropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;\n\nconst DropdownMenuLabel = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Label>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {\n inset?: boolean;\n }\n>(({ className, inset, ...props }, ref) => (\n <DropdownMenuPrimitive.Label\n ref={ref}\n className={cn(\n \"px-2 py-1.5 text-sm font-semibold text-foreground\",\n inset && \"pl-8\",\n className,\n )}\n {...props}\n />\n));\nDropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;\n\nconst DropdownMenuSeparator = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Separator>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.Separator\n ref={ref}\n className={cn(\"-mx-1 my-1 h-px bg-border\", className)}\n {...props}\n />\n));\nDropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;\n\nconst DropdownMenuShortcut = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLSpanElement>) => (\n <span\n className={cn(\n \"ml-auto text-xs tracking-widest text-muted-foreground\",\n className,\n )}\n {...props}\n />\n);\nDropdownMenuShortcut.displayName = \"DropdownMenuShortcut\";\n\nexport {\n DropdownMenu,\n DropdownMenuTrigger,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuCheckboxItem,\n DropdownMenuRadioItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuShortcut,\n DropdownMenuGroup,\n DropdownMenuPortal,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuRadioGroup,\n};\n"
16
+ },
17
+ {
18
+ "path": "components/base/DropdownMenu.md",
19
+ "type": "registry:file",
20
+ "target": "components/base/DropdownMenu.md",
21
+ "content": "# DropdownMenu — guía de uso\n\nMenú de **acciones** contextuales que se abre desde un disparador (Radix). Para agrupar acciones secundarias (\"⋯\", \"Opciones\") sin llenar la pantalla de botones. El componente vive en tu proyecto: instálalo con `create-lexy add dropdown-menu` y edítalo con libertad.\n\n## Import\n\n```tsx\nimport {\n DropdownMenu, DropdownMenuTrigger, DropdownMenuContent,\n DropdownMenuItem, DropdownMenuSeparator, DropdownMenuLabel,\n} from \"@/components/base/DropdownMenu\";\n```\n\n## Uso básico\n\n```tsx\n<DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"ghost\" size=\"icon\" aria-label=\"Más acciones\"><MoreHorizontal /></Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuLabel>Acciones</DropdownMenuLabel>\n <DropdownMenuItem onSelect={editar}>Editar</DropdownMenuItem>\n <DropdownMenuItem onSelect={duplicar}>Duplicar</DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuItem className=\"text-destructive\" onSelect={eliminar}>\n Eliminar\n </DropdownMenuItem>\n </DropdownMenuContent>\n</DropdownMenu>\n```\n\n## Exports\n\nRoot y trigger: `DropdownMenu`, `DropdownMenuTrigger`, `DropdownMenuContent`.\nÍtems: `DropdownMenuItem`, `DropdownMenuCheckboxItem`, `DropdownMenuRadioItem`, `DropdownMenuRadioGroup`.\nEstructura: `DropdownMenuLabel`, `DropdownMenuSeparator`, `DropdownMenuGroup`, `DropdownMenuShortcut`.\nSubmenús: `DropdownMenuSub`, `DropdownMenuSubTrigger`, `DropdownMenuSubContent`, `DropdownMenuPortal`.\n\n## Props (esenciales)\n\n| Parte | Prop | Descripción |\n|---|---|---|\n| `DropdownMenuTrigger` | `asChild` | Usa tu botón como disparador. |\n| `DropdownMenuContent` | `align` | `\"start\" \\| \"center\" \\| \"end\"`. |\n| `DropdownMenuItem` | `onSelect` | Acción al elegir. |\n| `DropdownMenuItem` | `disabled` | Deshabilita la acción. |\n| `DropdownMenuCheckboxItem` | `checked` / `onCheckedChange` | Alternar opción. |\n\n## Reglas\n\n- Es para **acciones**, no para seleccionar el valor de un formulario.\n- Da `aria-label` al trigger solo-icono.\n- Usa `DropdownMenuSeparator` para aislar acciones destructivas; márcalas con color **y** texto claro.\n- Agrupa con `DropdownMenuLabel`; no metas formularios largos dentro.\n- Submenús (`Sub`) solo para jerarquía real; evita anidar de más.\n\n## Cuándo NO usar\n\n- **Seleccionar un valor en un formulario** → `Select`.\n- **Buscar/filtrar opciones** → `Combobox` o `Command`.\n- **Contenido o formulario interactivo** → `Popover`.\n- **Navegación principal del sitio** → `NavigationMenu`.\n\n## Para IA\n\n1. Confirma que son acciones (verbos), no selección de datos.\n2. Usa `DropdownMenuTrigger asChild` sobre un `Button` (con `aria-label` si es icono).\n3. Ordena por frecuencia; separa y marca las destructivas.\n4. Si en realidad es selección de valor o búsqueda, cambia a `Select`/`Combobox`.\n"
22
+ }
23
+ ]
24
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "empty",
4
+ "type": "registry:ui",
5
+ "title": "Empty",
6
+ "description": "Estado vacío para cuando no hay datos, resultados o contenido que mostrar.",
7
+ "dependencies": [
8
+ "class-variance-authority"
9
+ ],
10
+ "registryDependencies": [],
11
+ "files": [
12
+ {
13
+ "path": "components/base/Empty.tsx",
14
+ "type": "registry:ui",
15
+ "content": "import * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nfunction Empty({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"empty\"\n className={cn(\n \"flex w-full min-w-0 flex-1 flex-col items-center justify-center gap-4 rounded-lg border-dashed p-12 text-center text-balance\",\n className\n )}\n {...props}\n />\n );\n}\n\nfunction EmptyHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"empty-header\"\n className={cn(\"flex max-w-sm flex-col items-center gap-2\", className)}\n {...props}\n />\n );\n}\n\nconst emptyMediaVariants = cva(\n \"mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0\",\n {\n variants: {\n variant: {\n default: \"bg-transparent\",\n icon: \"flex size-10 shrink-0 items-center justify-center rounded-lg bg-muted text-foreground [&_svg:not([class*='size-'])]:size-6\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n },\n }\n);\n\nfunction EmptyMedia({\n className,\n variant = \"default\",\n ...props\n}: React.ComponentProps<\"div\"> & VariantProps<typeof emptyMediaVariants>) {\n return (\n <div\n data-slot=\"empty-icon\"\n data-variant={variant}\n className={cn(emptyMediaVariants({ variant, className }))}\n {...props}\n />\n );\n}\n\nfunction EmptyTitle({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"empty-title\"\n className={cn(\n \"font-sans text-lg font-medium tracking-tight\",\n className\n )}\n {...props}\n />\n );\n}\n\nfunction EmptyDescription({ className, ...props }: React.ComponentProps<\"p\">) {\n return (\n <div\n data-slot=\"empty-description\"\n className={cn(\n \"text-sm/relaxed text-muted-foreground [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary\",\n className\n )}\n {...props}\n />\n );\n}\n\nfunction EmptyContent({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"empty-content\"\n className={cn(\n \"flex w-full max-w-sm min-w-0 flex-col items-center gap-4 text-sm text-balance\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport {\n Empty,\n EmptyHeader,\n EmptyTitle,\n EmptyDescription,\n EmptyContent,\n EmptyMedia,\n emptyMediaVariants,\n};\n"
16
+ },
17
+ {
18
+ "path": "components/base/Empty.md",
19
+ "type": "registry:file",
20
+ "target": "components/base/Empty.md",
21
+ "content": "# Empty — guía de uso\n\nEstado vacío para cuando no hay datos, resultados o contenido que mostrar.\nCompuesto por sub-componentes que permiten construir distintas variantes. El componente vive en tu proyecto: instálalo con `create-lexy add empty` y edítalo con libertad.\n\n## Import\n\n```tsx\nimport { Empty, EmptyHeader, EmptyTitle, EmptyDescription, EmptyContent, EmptyMedia } from \"@/shared/components/base/Empty\"; // o @/components/base/Empty\nimport { Inbox } from \"lucide-react\";\n```\n\n## Uso básico\n\n```tsx\n<Empty>\n <EmptyHeader>\n <EmptyTitle>Sin resultados</EmptyTitle>\n <EmptyDescription>\n No se encontraron elementos que coincidan con tu búsqueda.\n </EmptyDescription>\n </EmptyHeader>\n</Empty>\n```\n\n## Con icono\n\n```tsx\n<Empty>\n <EmptyHeader>\n <EmptyMedia variant=\"icon\">\n <Inbox className=\"h-6 w-6\" />\n </EmptyMedia>\n <EmptyTitle>Bandeja vacía</EmptyTitle>\n <EmptyDescription>\n No tienes mensajes pendientes.\n </EmptyDescription>\n </EmptyHeader>\n</Empty>\n```\n\n## Con acción\n\n```tsx\n<Empty>\n <EmptyHeader>\n <EmptyMedia variant=\"icon\">\n <Upload className=\"h-6 w-6\" />\n </EmptyMedia>\n <EmptyTitle>Sube tu primer archivo</EmptyTitle>\n <EmptyDescription>\n Arrastra archivos aquí o haz click para seleccionarlos.\n </EmptyDescription>\n </EmptyHeader>\n <EmptyContent>\n <Button>Seleccionar archivos</Button>\n </EmptyContent>\n</Empty>\n```\n\n## Props\n\n### Sub-componentes\n\n| Componente | Props | Descripción |\n|---|---|---|\n| `Empty` | `className?` | Contenedor principal. |\n| `EmptyHeader` | `className?` | Agrupa título, descripción e icono. |\n| `EmptyMedia` | `variant?: \"default\" \\| \"icon\"`, `className?` | Icono o media decorativa. |\n| `EmptyTitle` | `className?` | Título principal. |\n| `EmptyDescription` | `className?` | Texto explicativo. Soporta enlaces. |\n| `EmptyContent` | `className?` | Área para botones o acciones. |\n\n## Reglas\n\n- Usa `Empty` cuando no hay datos que mostrar.\n- No uses `Empty` para estados de carga → usa `Skeleton` o `Spinner`.\n- No uses `Empty` en espacios muy pequeños → usa un mensaje inline.\n- Ofrece una acción clara cuando sea posible (crear, buscar, filtrar).\n- Usa `variant=\"icon\"` en `EmptyMedia` para dar contexto visual.\n\n## Para IA\n\n1. Identifica si la pantalla o sección está vacía por falta de datos.\n2. Si es un estado vacío → usa `Empty`.\n3. Crea `EmptyHeader` con `EmptyTitle` y `EmptyDescription`.\n4. Agrega `EmptyMedia` con `variant=\"icon\"` si un icono aporta contexto.\n5. Agrega `EmptyContent` con botones o links cuando haya una acción posible.\n6. No uses `Empty` para estados de carga.\n7. Asegúrate de que el mensaje sea útil y no genérico.\n"
22
+ }
23
+ ]
24
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "feature-card",
4
+ "type": "registry:ui",
5
+ "title": "FeatureCard",
6
+ "description": "Tarjeta con icono y descripción. Para destacar características. El componente vive en tu proyecto: instálalo con `create-lexy add feature-card` y edítalo con libertad.",
7
+ "dependencies": [],
8
+ "registryDependencies": [],
9
+ "files": [
10
+ {
11
+ "path": "components/base/FeatureCard.tsx",
12
+ "type": "registry:ui",
13
+ "content": "import * as React from \"react\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nexport interface FeatureCardProps\n extends Omit<React.HTMLAttributes<HTMLElement>, \"title\"> {\n title: React.ReactNode;\n description?: React.ReactNode;\n /** Icono (cualquier nodo: SVG, lucide, etc.). */\n icon?: React.ReactNode;\n /** Si se define, renderiza como <a> con afordancia de hover/foco. */\n href?: string;\n target?: string;\n rel?: string;\n}\n\nconst cardClassName = (interactive: boolean) =>\n cn(\n \"group flex w-full flex-col gap-4 rounded-lg border border-border bg-background p-6 transition-[border-color,box-shadow] duration-150\",\n interactive &&\n \"cursor-pointer hover:border-primary/40 hover:shadow-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n );\n\nfunction FeatureCardContent({\n title,\n description,\n icon,\n interactive,\n}: {\n title: React.ReactNode;\n description?: React.ReactNode;\n icon?: React.ReactNode;\n interactive: boolean;\n}) {\n return (\n <>\n {(icon || interactive) && (\n <div className='flex items-start justify-between gap-3'>\n {icon ? (\n <span\n className='inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-md bg-accent text-primary [&>svg]:h-4 [&>svg]:w-4'\n aria-hidden>\n {icon}\n </span>\n ) : (\n <span className='min-w-0 flex-1' />\n )}\n {interactive && (\n <svg\n className='h-4 w-4 shrink-0 text-muted-foreground transition-transform group-hover:-translate-y-0.5 group-hover:translate-x-0.5 group-hover:text-foreground'\n viewBox='0 0 24 24'\n fill='none'\n stroke='currentColor'\n strokeWidth='2'\n strokeLinecap='round'\n strokeLinejoin='round'\n aria-hidden>\n <path d='M7 7h10v10M7 17 17 7' />\n </svg>\n )}\n </div>\n )}\n <div className='flex flex-col gap-1'>\n <h3 className='text-sm font-medium text-foreground'>{title}</h3>\n {description != null && description !== \"\" && (\n <p className='text-xs leading-relaxed text-muted-foreground'>\n {description}\n </p>\n )}\n </div>\n </>\n );\n}\n\nexport const FeatureCard = React.forwardRef<HTMLElement, FeatureCardProps>(\n function FeatureCard(\n { title, description, icon, href, target, rel, className, ...props },\n ref,\n ) {\n const interactive = href != null && href !== \"\";\n const classes = cn(cardClassName(interactive), className);\n\n if (interactive) {\n const resolvedRel =\n rel ?? (target === \"_blank\" ? \"noopener noreferrer\" : undefined);\n return (\n <a\n ref={ref as React.Ref<HTMLAnchorElement>}\n href={href}\n target={target}\n rel={resolvedRel}\n className={classes}\n {...(props as React.AnchorHTMLAttributes<HTMLAnchorElement>)}>\n <FeatureCardContent\n title={title}\n description={description}\n icon={icon}\n interactive\n />\n </a>\n );\n }\n\n return (\n <article\n ref={ref as React.Ref<HTMLElement>}\n className={classes}\n {...(props as React.HTMLAttributes<HTMLElement>)}>\n <FeatureCardContent\n title={title}\n description={description}\n icon={icon}\n interactive={false}\n />\n </article>\n );\n },\n);\n"
14
+ },
15
+ {
16
+ "path": "components/base/FeatureCard.md",
17
+ "type": "registry:file",
18
+ "target": "components/base/FeatureCard.md",
19
+ "content": "# FeatureCard — guía de uso\n\nTarjeta con icono y descripción. Para destacar características. El componente vive en tu proyecto: instálalo con `create-lexy add feature-card` y edítalo con libertad.\n\n## Import\n\n```tsx\nimport { FeatureCard } from \"@/components/base/FeatureCard\";\n```\n\n## Uso básico\n\n```tsx\n<FeatureCard />\n```\n\n## Props\n\nConsulta la story en Storybook para ver las props disponibles.\n\n## Reglas\n\n- Usa FeatureCard según el propósito descrito.\n- No abuses de este componente en contexts donde no aplica.\n\n## Para IA\n\n1. Identifica el contexto de uso del componente.\n2. Importa FeatureCard desde el path correcto.\n3. Configura las props según la necesidad.\n4. Verifica que el componente se integre correctamente en el layout.\n5. Solo usa variantes documentadas.\n"
20
+ }
21
+ ]
22
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "header-bar",
4
+ "type": "registry:ui",
5
+ "title": "HeaderBar",
6
+ "description": "Barra superior de aplicación o sitio: marca a la izquierda y acciones a la derecha. Renderiza un `<header>` semántico. El componente vive en tu proyecto: instálalo con `create-lexy add header-bar` y edítalo con libertad.",
7
+ "dependencies": [
8
+ "class-variance-authority"
9
+ ],
10
+ "registryDependencies": [
11
+ "logo"
12
+ ],
13
+ "files": [
14
+ {
15
+ "path": "components/base/HeaderBar.tsx",
16
+ "type": "registry:ui",
17
+ "content": "import * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"@/lib/utils/cn\";\nimport { Logo } from \"./Logo\";\n\n// `brand-navy` = navy/ink de marca Lexy (mismo del wordmark oscuro), definido\n// en el theme como --color-brand-navy.\nconst headerVariants = cva(\n \"flex h-16 w-full shrink-0 items-center justify-between gap-6\",\n {\n variants: {\n surface: {\n default: \"bg-background text-foreground\",\n transparent: \"bg-transparent text-foreground\",\n navy: \"bg-brand-navy text-white\",\n },\n bordered: { true: \"border-b\", false: \"\" },\n padding: {\n page: \"px-4 md:px-6 lg:px-8\",\n none: \"px-0\",\n },\n sticky: { true: \"sticky top-0 z-50\", false: \"\" },\n },\n compoundVariants: [\n { surface: \"default\", bordered: true, class: \"border-border\" },\n { surface: \"transparent\", bordered: true, class: \"border-border\" },\n { surface: \"navy\", bordered: true, class: \"border-white/15\" },\n ],\n defaultVariants: {\n surface: \"default\",\n bordered: true,\n padding: \"page\",\n sticky: false,\n },\n },\n);\n\nexport interface HeaderBarProps\n extends React.HTMLAttributes<HTMLElement>,\n VariantProps<typeof headerVariants> {\n /** Acciones alineadas a la derecha (botones, avatar, menús). */\n actions?: React.ReactNode;\n /** Sobrescribe el área de marca (por defecto, el `Logo`). */\n brand?: React.ReactNode;\n}\n\nexport const HeaderBar = React.forwardRef<HTMLElement, HeaderBarProps>(\n function HeaderBar(\n { surface = \"default\", bordered, padding, sticky, actions, brand, className, ...props },\n ref,\n ) {\n const logoSurface = surface === \"navy\" ? \"dark\" : \"light\";\n\n return (\n <header\n ref={ref}\n className={cn(\n headerVariants({ surface, bordered, padding, sticky }),\n className,\n )}\n {...props}>\n <div className='flex min-w-0 items-center gap-6'>\n {brand ?? <Logo surface={logoSurface} />}\n </div>\n {actions != null && (\n <div className='flex shrink-0 items-center gap-2'>{actions}</div>\n )}\n </header>\n );\n },\n);\n\nHeaderBar.displayName = \"HeaderBar\";\n\nexport { headerVariants };\n"
18
+ },
19
+ {
20
+ "path": "components/base/HeaderBar.md",
21
+ "type": "registry:file",
22
+ "target": "components/base/HeaderBar.md",
23
+ "content": "# HeaderBar — guía de uso\n\nBarra superior de aplicación o sitio: marca a la izquierda y acciones a la derecha. Renderiza un `<header>` semántico. El componente vive en tu proyecto: instálalo con `create-lexy add header-bar` y edítalo con libertad.\n\n## Import\n\n```tsx\nimport { HeaderBar } from \"@/components/base/HeaderBar\";\n```\n\n## Uso básico\n\n```tsx\n<HeaderBar\n actions={\n <>\n <Button variant=\"ghost\">Ingresar</Button>\n <Button>Crear cuenta</Button>\n </>\n }\n/>\n```\n\nPor defecto muestra el `Logo`. Sobrescribe la marca con `brand`:\n\n```tsx\n<HeaderBar brand={<Logo layout=\"horizontal\" />} actions={<UserMenu />} />\n```\n\n## Props\n\n| Prop | Tipo | Default | Descripción |\n|---|---|---|---|\n| `surface` | `\"default\" \\| \"transparent\" \\| \"navy\"` | `\"default\"` | Fondo. `navy` usa el ink de marca con texto blanco. |\n| `bordered` | `boolean` | `true` | Borde inferior. |\n| `padding` | `\"page\" \\| \"none\"` | `\"page\"` | Padding horizontal estándar o nulo. |\n| `sticky` | `boolean` | `false` | Fija la barra al hacer scroll. |\n| `actions` | `ReactNode` | — | Contenido alineado a la derecha. |\n| `brand` | `ReactNode` | `<Logo>` | Sobrescribe el área de marca. |\n\n## Reglas\n\n- Mantén pocas acciones a la derecha; agrupa el resto en un `DropdownMenu`.\n- `surface=\"navy\"` ajusta el logo a su versión clara automáticamente; no fuerces el color del logo.\n- Si es una app con barra lateral, usa `SidebarProvider` + `AppSidebar` + `SidebarInset` (ver AppSidebar.md).\n- `sticky` para navegación persistente; cuida no tapar contenido al anclar.\n\n## Cuándo NO usar\n\n- **Layout completo de app con sidebar** → `SidebarProvider` + `AppSidebar` + `SidebarInset` (ver AppSidebar.md).\n- **Navegación lateral** → `AppSidebar`.\n- **Menús de navegación con dropdowns ricos** → combínalo con `NavigationMenu` dentro de `actions`/`brand`.\n\n## Para IA\n\n1. Úsalo como cabecera de sitio o barra superior simple.\n2. Pon la marca (o deja el `Logo` por defecto) y 1–3 acciones en `actions`.\n3. Elige `surface`/`sticky` según el contexto; agrupa acciones extra en `DropdownMenu`.\n4. Si la pantalla es una app con sidebar, prefiere `SidebarProvider` + `AppSidebar` + `SidebarInset` en lugar de componer a mano.\n"
24
+ }
25
+ ]
26
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "input",
4
+ "type": "registry:ui",
5
+ "title": "Input",
6
+ "description": "Campo de texto de una línea. Renderiza un `<input>` nativo; acepta todos sus atributos (`type`, `value`, `onChange`, `placeholder`, `aria-*`). El componente vive en tu proyecto: instálalo con `create-lexy add input` y edítalo con libertad.",
7
+ "dependencies": [],
8
+ "registryDependencies": [],
9
+ "files": [
10
+ {
11
+ "path": "components/base/Input.tsx",
12
+ "type": "registry:ui",
13
+ "content": "import * as React from \"react\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nconst Input = React.forwardRef<HTMLInputElement, React.ComponentProps<\"input\">>(\n ({ className, type, ...props }, ref) => {\n return (\n <input\n type={type}\n className={cn(\n \"flex h-10 w-full rounded border border-input bg-background px-3 py-2 text-sm text-foreground outline-none transition-colors duration-150\",\n \"placeholder:text-muted-foreground\",\n \"file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground\",\n \"hover:border-muted-foreground/40\",\n \"focus-visible:border-primary\",\n \"aria-invalid:border-destructive aria-invalid:hover:border-destructive aria-invalid:focus-visible:border-destructive\",\n \"disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:border-input\",\n className,\n )}\n ref={ref}\n {...props}\n />\n );\n },\n);\nInput.displayName = \"Input\";\n\nexport { Input };\n"
14
+ },
15
+ {
16
+ "path": "components/base/Input.md",
17
+ "type": "registry:file",
18
+ "target": "components/base/Input.md",
19
+ "content": "# Input — guía de uso\n\nCampo de texto de una línea. Renderiza un `<input>` nativo; acepta todos sus atributos (`type`, `value`, `onChange`, `placeholder`, `aria-*`). El componente vive en tu proyecto: instálalo con `create-lexy add input` y edítalo con libertad.\n\n## Import\n\n```tsx\nimport { Input } from \"@/components/base/Input\";\n```\n\n## Uso básico\n\nSiempre con un `Label` asociado por `id`/`htmlFor`:\n\n```tsx\n<div className=\"grid gap-1.5\">\n <Label htmlFor=\"email\">Email</Label>\n <Input id=\"email\" type=\"email\" placeholder=\"nombre@empresa.cl\" />\n</div>\n```\n\n## Estado de error\n\n```tsx\n<div className=\"grid gap-1.5\">\n <Label htmlFor=\"rut\">RUT</Label>\n <Input id=\"rut\" aria-invalid aria-describedby=\"rut-error\" />\n <p id=\"rut-error\" className=\"text-sm text-destructive\">\n Ingresa un RUT válido, por ejemplo 12.345.678-9.\n </p>\n</div>\n```\n\n## Props\n\n| Prop | Tipo | Default | Descripción |\n|---|---|---|---|\n| `type` | `string` | `\"text\"` | `text`, `email`, `password`, `number`, `search`, `file`, etc. |\n| `aria-invalid` | `boolean` | — | Activa el estilo de error (borde destructive). |\n| `disabled` | `boolean` | `false` | Deshabilita el campo. |\n| ...resto | `input` props | — | `value`, `onChange`, `placeholder`, `name`, `required`, etc. |\n\n## Reglas\n\n- **Siempre** asocia un `Label` por `htmlFor`/`id`. El `placeholder` no es un label: desaparece al escribir.\n- Marca el error con `aria-invalid` **y** un mensaje de texto cercano; el color del borde no basta (accesibilidad).\n- Enlaza el mensaje de error con `aria-describedby` para lectores de pantalla.\n- Usa el `type` correcto (`email`, `number`, `tel`): mejora el teclado móvil y la validación.\n\n## Cuándo NO usar\n\n- **Texto largo / multilínea** → `Textarea`.\n- **Elegir de una lista fija** → `Select` (o `Combobox` si hay búsqueda).\n- **Sí/no o selección múltiple** → `Checkbox` / `RadioGroup` / `Switch`.\n- **Buscar con icono y limpiar** → `Searchbox`.\n\n## Para IA\n\n1. Confirma que el dato es texto corto de una línea; si no, elige el control adecuado (ver arriba).\n2. Crea el par `Label` + `Input` con `htmlFor`/`id` coincidentes.\n3. Elige `type` según el dato.\n4. Para validación, usa `aria-invalid` + mensaje con `aria-describedby`, redactado como corregir (no solo \"campo inválido\").\n5. No uses el `placeholder` como única etiqueta.\n"
20
+ }
21
+ ]
22
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "label",
4
+ "type": "registry:ui",
5
+ "title": "Label",
6
+ "description": "Etiqueta para campos de formulario. Incluye indicador de requerido. El componente vive en tu proyecto: instálalo con `create-lexy add label` y edítalo con libertad.",
7
+ "dependencies": [
8
+ "@radix-ui/react-label",
9
+ "class-variance-authority"
10
+ ],
11
+ "registryDependencies": [],
12
+ "files": [
13
+ {
14
+ "path": "components/base/Label.tsx",
15
+ "type": "registry:ui",
16
+ "content": "import * as React from \"react\";\nimport * as LabelPrimitive from \"@radix-ui/react-label\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nconst labelVariants = cva(\n \"text-sm font-medium leading-none text-foreground peer-disabled:cursor-not-allowed peer-disabled:opacity-70\",\n);\n\nconst Label = React.forwardRef<\n React.ElementRef<typeof LabelPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &\n VariantProps<typeof labelVariants> & {\n required?: boolean;\n }\n>(({ className, required, children, ...props }, ref) => (\n <LabelPrimitive.Root\n ref={ref}\n className={cn(labelVariants(), className)}\n {...props}\n >\n {children}\n {required && (\n <span aria-hidden=\"true\" className=\"ml-0.5 text-destructive\">\n *\n </span>\n )}\n </LabelPrimitive.Root>\n));\nLabel.displayName = LabelPrimitive.Root.displayName;\n\nexport { Label, labelVariants };\n"
17
+ },
18
+ {
19
+ "path": "components/base/Label.md",
20
+ "type": "registry:file",
21
+ "target": "components/base/Label.md",
22
+ "content": "# Label — guía de uso\n\nEtiqueta para campos de formulario. Incluye indicador de requerido. El componente vive en tu proyecto: instálalo con `create-lexy add label` y edítalo con libertad.\n\n## Import\n\n```tsx\nimport { Label } from \"@/components/base/Label\";\n```\n\n## Uso básico\n\n```tsx\n<Label />\n```\n\n## Props\n\nConsulta la story en Storybook para ver las props disponibles.\n\n## Reglas\n\n- Usa Label según el propósito descrito.\n- No abuses de este componente en contexts donde no aplica.\n\n## Para IA\n\n1. Identifica el contexto de uso del componente.\n2. Importa Label desde el path correcto.\n3. Configura las props según la necesidad.\n4. Verifica que el componente se integre correctamente en el layout.\n5. Solo usa variantes documentadas.\n"
23
+ }
24
+ ]
25
+ }