create-lexy 0.5.1 → 0.6.1

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 (66) hide show
  1. package/assets/r/accordion.json +1 -1
  2. package/assets/r/alert-dialog.json +1 -1
  3. package/assets/r/app-accordion.json +1 -1
  4. package/assets/r/app-dialog.json +1 -1
  5. package/assets/r/app-header-bar.json +1 -1
  6. package/assets/r/app-sidebar.json +1 -1
  7. package/assets/r/avatar.json +1 -1
  8. package/assets/r/badge.json +1 -1
  9. package/assets/r/brand-background.json +135 -0
  10. package/assets/r/breadcrumb.json +1 -1
  11. package/assets/r/button-group.json +1 -1
  12. package/assets/r/button.json +1 -1
  13. package/assets/r/calendar.json +1 -1
  14. package/assets/r/card.json +1 -1
  15. package/assets/r/chart.json +1 -1
  16. package/assets/r/checkbox.json +1 -1
  17. package/assets/r/combobox.json +1 -1
  18. package/assets/r/command.json +1 -1
  19. package/assets/r/confirmacion.json +2 -2
  20. package/assets/r/counter-badge.json +1 -1
  21. package/assets/r/crm-app-layout.json +2 -2
  22. package/assets/r/crm-desk.json +2 -2
  23. package/assets/r/crm-detalle-caso.json +2 -2
  24. package/assets/r/date-picker.json +1 -1
  25. package/assets/r/dialog.json +1 -1
  26. package/assets/r/dropdown-menu.json +1 -1
  27. package/assets/r/empty.json +1 -1
  28. package/assets/r/feature-card.json +1 -1
  29. package/assets/r/form.json +1 -1
  30. package/assets/r/header-bar.json +1 -1
  31. package/assets/r/input.json +1 -1
  32. package/assets/r/intake-wizard.json +2 -2
  33. package/assets/r/label.json +1 -1
  34. package/assets/r/login.json +2 -2
  35. package/assets/r/logo.json +1 -1
  36. package/assets/r/menubar.json +1 -1
  37. package/assets/r/navigation-menu.json +1 -1
  38. package/assets/r/pagination.json +1 -1
  39. package/assets/r/popover.json +1 -1
  40. package/assets/r/profile-card.json +1 -1
  41. package/assets/r/progress.json +1 -1
  42. package/assets/r/radio-group.json +1 -1
  43. package/assets/r/registry.json +116 -0
  44. package/assets/r/scroll-area.json +1 -1
  45. package/assets/r/searchbox.json +1 -1
  46. package/assets/r/select.json +1 -1
  47. package/assets/r/separator.json +1 -1
  48. package/assets/r/sheet.json +1 -1
  49. package/assets/r/sidebar.json +2 -2
  50. package/assets/r/skeleton.json +1 -1
  51. package/assets/r/slider.json +1 -1
  52. package/assets/r/snippet.json +1 -1
  53. package/assets/r/spinner.json +1 -1
  54. package/assets/r/status-dot.json +1 -1
  55. package/assets/r/switch.json +1 -1
  56. package/assets/r/table.json +1 -1
  57. package/assets/r/tabs.json +1 -1
  58. package/assets/r/tag.json +1 -1
  59. package/assets/r/textarea.json +1 -1
  60. package/assets/r/toaster.json +1 -1
  61. package/assets/r/tooltip.json +1 -1
  62. package/assets/r/tree.json +1 -1
  63. package/assets/registry-version +1 -1
  64. package/assets/theme/lexy-theme.css +15 -4
  65. package/dist/index.js +2421 -70
  66. package/package.json +16 -5
@@ -19,7 +19,7 @@
19
19
  "path": "components/base/Accordion.md",
20
20
  "type": "registry:file",
21
21
  "target": "components/base/Accordion.md",
22
- "content": "# Accordion — guía de uso\n\nSecciones colapsables para mostrar detalle bajo demanda (Radix). Progressive disclosure: muestra primero los títulos y despliega el contenido al pedirlo. El componente vive en tu proyecto: instálalo con `create-lexy add accordion` y edítalo con libertad.\n\n## Import\n\n```tsx\nimport {\n Accordion, AccordionItem, AccordionTrigger, AccordionContent,\n} from \"@/components/base/Accordion\";\n```\n\n## Uso básico\n\n```tsx\n<Accordion type=\"single\" collapsible>\n <AccordionItem value=\"envio\">\n <AccordionTrigger>¿Cuánto tarda el envío?</AccordionTrigger>\n <AccordionContent>Entre 3 y 5 días hábiles.</AccordionContent>\n </AccordionItem>\n <AccordionItem value=\"devolucion\">\n <AccordionTrigger>¿Puedo devolver un producto?</AccordionTrigger>\n <AccordionContent>Sí, dentro de 30 días.</AccordionContent>\n </AccordionItem>\n</Accordion>\n```\n\n## Varias secciones abiertas\n\n```tsx\n<Accordion type=\"multiple\" defaultValue={[\"envio\"]}>\n …\n</Accordion>\n```\n\n## Props (esenciales)\n\n| Parte | Prop | Descripción |\n|---|---|---|\n| `Accordion` | `type` | `\"single\"` (una abierta) o `\"multiple\"` (varias). **Requerido.** |\n| `Accordion` | `collapsible` | En `single`, permite cerrar la abierta. |\n| `Accordion` | `value` / `defaultValue` | Sección(es) abierta(s). |\n| `AccordionItem` | `value` | **Requerido.** Identificador único. |\n\n## Reglas\n\n- Cada `AccordionItem` necesita `value` único.\n- El `AccordionTrigger` lleva un título escaneable (sentence case), no una frase larga.\n- No escondas información crítica (costos, errores, próximos pasos) dentro de secciones cerradas.\n- Úsalo para detalle secundario u opcional; el contenido principal va visible.\n\n## Cuándo NO usar\n\n- **Categorías pares que se alternan** → `Tabs`.\n- **Una sola sección colapsable puntual** → basta un toggle simple; el accordion es para varias.\n- **Navegación lateral con jerarquía** → `AppSidebar` (grupos colapsables).\n- **Acción puntual en una capa** → `Dialog`/`Sheet`.\n\n## Para IA\n\n1. Úsalo para FAQ o detalle opcional que conviene plegar.\n2. Elige `type=\"single\" collapsible` para una a la vez, `multiple` si conviene comparar.\n3. Da `value` único a cada ítem y títulos escaneables al trigger.\n4. Nunca ocultes información bloqueante o de riesgo dentro del accordion.\n"
22
+ "content": "# Accordion — guía de uso\n\nSecciones colapsables para mostrar detalle bajo demanda (Radix). Progressive disclosure: muestra primero los títulos y despliega el contenido al pedirlo. El componente vive en tu proyecto: instálalo con `create-lexy add accordion` y edítalo con libertad.\n\n## Cuándo usarlo\n\nPara revelar detalle bajo demanda cuando los títulos bastan para orientar: FAQs, secciones de ayuda, metadatos de un caso. Progressive disclosure: la persona ve primero el mapa (los títulos) y abre solo lo que le interesa, sin abandonar la página.\n\n## Composición\n\nCuatro piezas: `Accordion` (contenedor que gobierna el estado), un `AccordionItem` por sección, y dentro de cada una `AccordionTrigger` (el título clickeable con chevron) y `AccordionContent` (el detalle que se pliega). Para el caso data-driven usa `AppAccordion`, que arma esta composición desde un arreglo de items.\n\n## Uso básico\n\n```tsx\n<Accordion type=\"single\" collapsible>\n <AccordionItem value=\"envio\">\n <AccordionTrigger>¿Cuánto tarda el envío?</AccordionTrigger>\n <AccordionContent>Entre 3 y 5 días hábiles.</AccordionContent>\n </AccordionItem>\n <AccordionItem value=\"devolucion\">\n <AccordionTrigger>¿Puedo devolver un producto?</AccordionTrigger>\n <AccordionContent>Sí, dentro de 30 días.</AccordionContent>\n </AccordionItem>\n</Accordion>\n```\n\n## Varias secciones abiertas\n\n```tsx\n<Accordion type=\"multiple\" defaultValue={[\"envio\"]}>\n …\n</Accordion>\n```\n\n## Reglas\n\n- Cada `AccordionItem` necesita `value` único.\n- El `AccordionTrigger` lleva un título escaneable (sentence case), no una frase larga.\n- No escondas información crítica (costos, errores, próximos pasos) dentro de secciones cerradas.\n- Úsalo para detalle secundario u opcional; el contenido principal va visible.\n\n## Cuándo NO usar\n\n- **Categorías pares que se alternan** → `Tabs`.\n- **Una sola sección colapsable puntual** → basta un toggle simple; el accordion es para varias.\n- **Navegación lateral con jerarquía** → `AppSidebar` (grupos colapsables).\n- **Acción puntual en una capa** → `Dialog`/`Sheet`.\n\n## Import\n\n```tsx\nimport {\n Accordion, AccordionItem, AccordionTrigger, AccordionContent,\n} from \"@/components/base/Accordion\";\n```\n\n## Props (esenciales)\n\n| Parte | Prop | Descripción |\n|---|---|---|\n| `Accordion` | `type` | `\"single\"` (una abierta) o `\"multiple\"` (varias). **Requerido.** |\n| `Accordion` | `collapsible` | En `single`, permite cerrar la abierta. |\n| `Accordion` | `value` / `defaultValue` | Sección(es) abierta(s). |\n| `AccordionItem` | `value` | **Requerido.** Identificador único. |\n\n## Para IA\n\n1. Úsalo para FAQ o detalle opcional que conviene plegar.\n2. Elige `type=\"single\" collapsible` para una a la vez, `multiple` si conviene comparar.\n3. Da `value` único a cada ítem y títulos escaneables al trigger.\n4. Nunca ocultes información bloqueante o de riesgo dentro del accordion.\n"
23
23
  }
24
24
  ]
25
25
  }
@@ -22,7 +22,7 @@
22
22
  "path": "components/base/AlertDialog.md",
23
23
  "type": "registry:file",
24
24
  "target": "components/base/AlertDialog.md",
25
- "content": "# AlertDialog — guía de uso\n\nConfirmación modal que **interrumpe** (rol `alertdialog`): la persona debe elegir entre cancelar o confirmar — no hay X, ni cierre con click afuera. Para destruir, descartar cambios o salir de un flujo con costo. El componente vive en tu proyecto: instálalo con `create-lexy add alert-dialog` y edítalo con libertad.\n\n## Import\n\n```tsx\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n AlertDialogTrigger,\n} from \"@/components/base/AlertDialog\";\n```\n\n## Uso básico (acción destructiva)\n\n```tsx\n<AlertDialog>\n <AlertDialogTrigger asChild>\n <Button variant=\"destructive\">Eliminar contrato</Button>\n </AlertDialogTrigger>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>¿Eliminar este contrato?</AlertDialogTitle>\n <AlertDialogDescription>\n Se eliminará de forma permanente, junto con sus documentos adjuntos.\n Esta acción no se puede deshacer.\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel>Cancelar</AlertDialogCancel>\n <AlertDialogAction variant=\"destructive\" onClick={eliminar}>\n Eliminar contrato\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n</AlertDialog>\n```\n\n## Confirmación no destructiva\n\n```tsx\n<AlertDialogAction onClick={confirmar}>Enviar ahora</AlertDialogAction>\n```\n\n`AlertDialogAction` acepta las variantes de `Button` (`variant`/`size`); por defecto es la acción primaria. `AlertDialogCancel` siempre es `ghost`.\n\n## Props\n\n| Pieza | Qué es |\n|---|---|\n| `AlertDialog` / `AlertDialogTrigger` | Primitivas de Radix (`asChild` disponible en el trigger). |\n| `AlertDialogContent` | Panel modal, mismo estilo que `DialogContent`. `className`, `...props`, `ref`. |\n| `AlertDialogHeader` / `AlertDialogFooter` | Layout de título/descripcion y acciones. |\n| `AlertDialogTitle` / `AlertDialogDescription` | Texto accesible del diálogo (obligatorios). |\n| `AlertDialogAction` | Botón que confirma y cierra. `variant`/`size` de `buttonVariants`. |\n| `AlertDialogCancel` | Botón que cancela y cierra (ghost). |\n\n## AlertDialog vs Dialog\n\n- **AlertDialog**: decisión bloqueante con consecuencias (eliminar, descartar, salir sin guardar). Escape/click afuera **no** cierran; siempre dos salidas explícitas.\n- **Dialog / AppDialog**: trabajo dentro de un modal (formularios, detalle, selección). Se puede cerrar con Escape o click en el overlay sin perder nada importante.\n- Si el \"confirmar\" requiere ingresar datos (motivo, contraseña), ya no es un AlertDialog: usa `Dialog` con formulario.\n\n## Reglas\n\n- El título es la pregunta concreta (*\"¿Eliminar este contrato?\"*), la descripción explica la consecuencia (*\"…no se puede deshacer\"*).\n- La acción repite el verbo del título (*\"Eliminar contrato\"*), nunca \"Aceptar\"/\"OK\".\n- Acción irreversible → `variant=\"destructive\"`.\n- No abuses: reserva la interrupción para decisiones con costo real; el éxito posterior se comunica con `Toaster`.\n\n## Para IA\n\n1. Úsalo solo para confirmar acciones con consecuencias; el resto va en `Dialog`/`AppDialog`.\n2. Título = pregunta con el verbo de la acción; descripción = consecuencia concreta; botón = mismo verbo.\n3. `AlertDialogTrigger asChild` sobre un `Button` existente.\n4. Tras confirmar, comunica el resultado (toast de éxito o error).\n"
25
+ "content": "# AlertDialog — guía de uso\n\nConfirmación modal que **interrumpe** (rol `alertdialog`): la persona debe elegir entre cancelar o confirmar — no hay X, ni cierre con click afuera. Para destruir, descartar cambios o salir de un flujo con costo. El componente vive en tu proyecto: instálalo con `create-lexy add alert-dialog` y edítalo con libertad.\n\n## Cuándo usarlo\n\nPara confirmaciones que interrumpen de verdad: destruir, descartar cambios, salir de un flujo con costo. No hay X ni cierre con click afuera — la persona debe decidir. Si la acción es reversible o informativa, `AppDialog` basta.\n\n## Composición\n\nComposición Radix: `AlertDialogTrigger` abre `AlertDialogContent`, con `AlertDialogHeader` (`AlertDialogTitle`, `AlertDialogDescription`) y `AlertDialogFooter` con `AlertDialogCancel` y `AlertDialogAction`.\n\n## Uso básico (acción destructiva)\n\n```tsx\n<AlertDialog>\n <AlertDialogTrigger asChild>\n <Button variant=\"destructive\">Eliminar contrato</Button>\n </AlertDialogTrigger>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>¿Eliminar este contrato?</AlertDialogTitle>\n <AlertDialogDescription>\n Se eliminará de forma permanente, junto con sus documentos adjuntos.\n Esta acción no se puede deshacer.\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel>Cancelar</AlertDialogCancel>\n <AlertDialogAction variant=\"destructive\" onClick={eliminar}>\n Eliminar contrato\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n</AlertDialog>\n```\n\n## Confirmación no destructiva\n\n```tsx\n<AlertDialogAction onClick={confirmar}>Enviar ahora</AlertDialogAction>\n```\n\n`AlertDialogAction` acepta las variantes de `Button` (`variant`/`size`); por defecto es la acción primaria. `AlertDialogCancel` siempre es `ghost`.\n\n## AlertDialog vs Dialog\n\n- **AlertDialog**: decisión bloqueante con consecuencias (eliminar, descartar, salir sin guardar). Escape/click afuera **no** cierran; siempre dos salidas explícitas.\n- **Dialog / AppDialog**: trabajo dentro de un modal (formularios, detalle, selección). Se puede cerrar con Escape o click en el overlay sin perder nada importante.\n- Si el \"confirmar\" requiere ingresar datos (motivo, contraseña), ya no es un AlertDialog: usa `Dialog` con formulario.\n\n## Reglas\n\n- El título es la pregunta concreta (*\"¿Eliminar este contrato?\"*), la descripción explica la consecuencia (*\"…no se puede deshacer\"*).\n- La acción repite el verbo del título (*\"Eliminar contrato\"*), nunca \"Aceptar\"/\"OK\".\n- Acción irreversible → `variant=\"destructive\"`.\n- No abuses: reserva la interrupción para decisiones con costo real; el éxito posterior se comunica con `Toaster`.\n\n## Import\n\n```tsx\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n AlertDialogTrigger,\n} from \"@/components/base/AlertDialog\";\n```\n\n## Props\n\n| Pieza | Qué es |\n|---|---|\n| `AlertDialog` / `AlertDialogTrigger` | Primitivas de Radix (`asChild` disponible en el trigger). |\n| `AlertDialogContent` | Panel modal, mismo estilo que `DialogContent`. `className`, `...props`, `ref`. |\n| `AlertDialogHeader` / `AlertDialogFooter` | Layout de título/descripcion y acciones. |\n| `AlertDialogTitle` / `AlertDialogDescription` | Texto accesible del diálogo (obligatorios). |\n| `AlertDialogAction` | Botón que confirma y cierra. `variant`/`size` de `buttonVariants`. |\n| `AlertDialogCancel` | Botón que cancela y cierra (ghost). |\n\n## Para IA\n\n1. Úsalo solo para confirmar acciones con consecuencias; el resto va en `Dialog`/`AppDialog`.\n2. Título = pregunta con el verbo de la acción; descripción = consecuencia concreta; botón = mismo verbo.\n3. `AlertDialogTrigger asChild` sobre un `Button` existente.\n4. Tras confirmar, comunica el resultado (toast de éxito o error).\n"
26
26
  }
27
27
  ]
28
28
  }
@@ -19,7 +19,7 @@
19
19
  "path": "components/base/AppAccordion.md",
20
20
  "type": "registry:file",
21
21
  "target": "components/base/AppAccordion.md",
22
- "content": "# AppAccordion — guía de uso\n\nAcordeón de aplicación **data-driven**. Describe los items como datos (`items`)\ny el componente compone internamente las primitivas de `Accordion`. **Esta es la\nvía a usar para construir acordeones.** No compongas a mano `AccordionItem`,\n`AccordionTrigger` y `AccordionContent` si `AppAccordion` cubre el caso. El componente vive en tu proyecto: instálalo con `create-lexy add app-accordion` y edítalo con libertad.\n\n## Import\n\n```tsx\nimport { AppAccordion } from \"@/components/base/AppAccordion\";\n```\n\n## Uso básico\n\n```tsx\n<AppAccordion\n items={[\n {\n value: \"item-1\",\n title: \"¿Qué es Lexy?\",\n content: \"Lexy es un sistema de diseño para construir interfaces consistentes.\",\n },\n {\n value: \"item-2\",\n title: \"¿Cómo instalar?\",\n content: \"Usa create-lexy para inicializar un proyecto.\",\n },\n ]}\n/>\n```\n\n## Uso múltiple\n\n```tsx\n<AppAccordion\n type=\"multiple\"\n items={[\n {\n value: \"filters\",\n title: \"Filtros\",\n content: <FilterForm />,\n },\n {\n value: \"sort\",\n title: \"Ordenamiento\",\n content: <SortOptions />,\n },\n ]}\n/>\n```\n\n## Con iconos\n\n```tsx\nimport { CircleHelp, Settings } from \"lucide-react\";\n\n<AppAccordion\n items={[\n {\n value: \"help\",\n title: \"Ayuda\",\n icon: <CircleHelp className=\"h-4 w-4\" />,\n content: \"Respuestas a preguntas frecuentes.\",\n },\n {\n value: \"settings\",\n title: \"Configuración\",\n icon: <Settings className=\"h-4 w-4\" />,\n content: \"Ajusta las preferencias de tu cuenta.\",\n },\n ]}\n/>\n```\n\n## Containered\n\n```tsx\n<AppAccordion\n containered\n items={[\n { value: \"faq-1\", title: \"¿Cómo creo una cuenta?\", content: \"Haz click en 'Crear cuenta'.\" },\n { value: \"faq-2\", title: \"¿Puedo cancelar?\", content: \"Sí, desde Configuración.\" },\n ]}\n/>\n```\n\n## Props\n\n### `AppAccordion`\n\n| Prop | Tipo | Default | Descripción |\n|---|---|---|---|\n| `items` | `AppAccordionItem[]` | — | **Requerido.** Items del acordeón. |\n| `type` | `\"single\" \\| \"multiple\"` | `\"single\"` | Tipo de acordeón. |\n| `defaultValue` | `string?` | — | Valor abierto por defecto (modo \"single\"). |\n| `defaultValues` | `string[]?` | — | Valores abiertos por defecto (modo \"multiple\"). |\n| `onValueChange` | `(value) => void` | — | Callback al cambiar item abierto. |\n| `containered` | `boolean` | `false` | Envuelve en contenedor con borde, fondo y bordes redondeados. |\n| `collapsible` | `boolean` | `true` | Permite colapsar el item abierto (modo \"single\"). |\n| `className` | `string?` | — | Clase adicional para el contenedor. |\n| `itemClassName` | `string?` | — | Clase adicional para cada item. |\n\n### `AppAccordionItem`\n\n| Campo | Tipo | Descripción |\n|---|---|---|\n| `value` | `string` | Identificador único del item. |\n| `title` | `string` | Título visible del item. |\n| `icon` | `ReactNode?` | Icono decorativo antes del título. |\n| `content` | `ReactNode` | Contenido del item. |\n| `disabled` | `boolean?` | Deshabilita el item. |\n\n## Receta compuesta\n\n`AppAccordion` es un wrapper ergonómico sobre el `Accordion` compuesto: las props extra (`aria-*`, `id`…) pasan al nodo raíz y el `ref` apunta a ese nodo. Si necesitas variar la estructura (triggers custom, contenido anidado, estilos por item), usa las piezas directamente:\n\n```tsx\nimport { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from \"@/components/base/Accordion\";\n```\n\n## Reglas\n\n- Define los items como datos y pásalos en `items`.\n- Usa `type=\"multiple\"` si necesitas que varios items estén abiertos simultáneamente.\n- Usa `value` como identificador único (no como índice).\n- `content` puede ser texto simple o componentes complejos (formularios, etc.).\n- Usa `disabled` para items no disponibles.\n- Para casos a medida que no encajen en `items`, usa las primitivas de `Accordion` directamente.\n\n## Para IA\n\n1. Identifica si el acordeón es FAQ, filtros, configuración o navegación.\n2. Si es FAQ/configuración → usa `AppAccordion`.\n3. Crea `AppAccordionItem[]` con `value`, `title` y `content`.\n4. Usa `icon` para agregar un icono decorativo antes del título de cada item.\n5. Usa `containered` para envolver el acordeón en un contenedor visual (borde, fondo, bordes redondeados).\n6. Usa `type=\"multiple\"` si el usuario necesita comparar contenido entre items.\n7. Usa `defaultValue` para abrir el primer item por defecto (útil para FAQs).\n8. Si el contenido es un formulario, pasa el componente como `content`.\n9. Solo usa `Accordion` primitivo si necesitas animaciones custom o layouts atípicos.\n"
22
+ "content": "# AppAccordion — guía de uso\n\nAcordeón de aplicación **data-driven**. Describe los items como datos (`items`)\ny el componente compone internamente las primitivas de `Accordion`. **Esta es la\nvía a usar para construir acordeones.** No compongas a mano `AccordionItem`,\n`AccordionTrigger` y `AccordionContent` si `AppAccordion` cubre el caso. El componente vive en tu proyecto: instálalo con `create-lexy add app-accordion` y edítalo con libertad.\n\n## Cuándo usarlo\n\nPara acordeones descritos como datos: FAQs, filtros colapsables, paneles de ayuda. Describes los items (título, contenido, icono) y el componente arma la composición — ideal para contenido que viene de un catálogo o CMS.\n\n## Composición\n\nUna sola pieza data-driven (`AppAccordion`) que arma por debajo la composición de `Accordion`: recibe `items` (título, contenido, icono opcional), modos single/multiple y variante containered.\n\n## Uso básico\n\n```tsx\n<AppAccordion\n items={[\n {\n value: \"item-1\",\n title: \"¿Qué es Lexy?\",\n content: \"Lexy es un sistema de diseño para construir interfaces consistentes.\",\n },\n {\n value: \"item-2\",\n title: \"¿Cómo instalar?\",\n content: \"Usa create-lexy para inicializar un proyecto.\",\n },\n ]}\n/>\n```\n\n## Uso múltiple\n\n```tsx\n<AppAccordion\n type=\"multiple\"\n items={[\n {\n value: \"filters\",\n title: \"Filtros\",\n content: <FilterForm />,\n },\n {\n value: \"sort\",\n title: \"Ordenamiento\",\n content: <SortOptions />,\n },\n ]}\n/>\n```\n\n## Con iconos\n\n```tsx\nimport { CircleHelp, Settings } from \"lucide-react\";\n\n<AppAccordion\n items={[\n {\n value: \"help\",\n title: \"Ayuda\",\n icon: <CircleHelp className=\"h-4 w-4\" />,\n content: \"Respuestas a preguntas frecuentes.\",\n },\n {\n value: \"settings\",\n title: \"Configuración\",\n icon: <Settings className=\"h-4 w-4\" />,\n content: \"Ajusta las preferencias de tu cuenta.\",\n },\n ]}\n/>\n```\n\n## Containered\n\n```tsx\n<AppAccordion\n containered\n items={[\n { value: \"faq-1\", title: \"¿Cómo creo una cuenta?\", content: \"Haz click en 'Crear cuenta'.\" },\n { value: \"faq-2\", title: \"¿Puedo cancelar?\", content: \"Sí, desde Configuración.\" },\n ]}\n/>\n```\n\n## Receta compuesta\n\n`AppAccordion` es un wrapper ergonómico sobre el `Accordion` compuesto: las props extra (`aria-*`, `id`…) pasan al nodo raíz y el `ref` apunta a ese nodo. Si necesitas variar la estructura (triggers custom, contenido anidado, estilos por item), usa las piezas directamente:\n\n```tsx\nimport { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from \"@/components/base/Accordion\";\n```\n\n## Reglas\n\n- Define los items como datos y pásalos en `items`.\n- Usa `type=\"multiple\"` si necesitas que varios items estén abiertos simultáneamente.\n- Usa `value` como identificador único (no como índice).\n- `content` puede ser texto simple o componentes complejos (formularios, etc.).\n- Usa `disabled` para items no disponibles.\n- Para casos a medida que no encajen en `items`, usa las primitivas de `Accordion` directamente.\n\n## Import\n\n```tsx\nimport { AppAccordion } from \"@/components/base/AppAccordion\";\n```\n\n## Props\n\n### `AppAccordion`\n\n| Prop | Tipo | Default | Descripción |\n|---|---|---|---|\n| `items` | `AppAccordionItem[]` | — | **Requerido.** Items del acordeón. |\n| `type` | `\"single\" \\| \"multiple\"` | `\"single\"` | Tipo de acordeón. |\n| `defaultValue` | `string?` | — | Valor abierto por defecto (modo \"single\"). |\n| `defaultValues` | `string[]?` | — | Valores abiertos por defecto (modo \"multiple\"). |\n| `onValueChange` | `(value) => void` | — | Callback al cambiar item abierto. |\n| `containered` | `boolean` | `false` | Envuelve en contenedor con borde, fondo y bordes redondeados. |\n| `collapsible` | `boolean` | `true` | Permite colapsar el item abierto (modo \"single\"). |\n| `className` | `string?` | — | Clase adicional para el contenedor. |\n| `itemClassName` | `string?` | — | Clase adicional para cada item. |\n\n### `AppAccordionItem`\n\n| Campo | Tipo | Descripción |\n|---|---|---|\n| `value` | `string` | Identificador único del item. |\n| `title` | `string` | Título visible del item. |\n| `icon` | `ReactNode?` | Icono decorativo antes del título. |\n| `content` | `ReactNode` | Contenido del item. |\n| `disabled` | `boolean?` | Deshabilita el item. |\n\n## Para IA\n\n1. Identifica si el acordeón es FAQ, filtros, configuración o navegación.\n2. Si es FAQ/configuración → usa `AppAccordion`.\n3. Crea `AppAccordionItem[]` con `value`, `title` y `content`.\n4. Usa `icon` para agregar un icono decorativo antes del título de cada item.\n5. Usa `containered` para envolver el acordeón en un contenedor visual (borde, fondo, bordes redondeados).\n6. Usa `type=\"multiple\"` si el usuario necesita comparar contenido entre items.\n7. Usa `defaultValue` para abrir el primer item por defecto (útil para FAQs).\n8. Si el contenido es un formulario, pasa el componente como `content`.\n9. Solo usa `Accordion` primitivo si necesitas animaciones custom o layouts atípicos.\n"
23
23
  }
24
24
  ]
25
25
  }
@@ -20,7 +20,7 @@
20
20
  "path": "components/base/AppDialog.md",
21
21
  "type": "registry:file",
22
22
  "target": "components/base/AppDialog.md",
23
- "content": "# AppDialog — guía de uso\n\nDiálogo de aplicación al estilo **Material 3**. Estructura canónica sin dividers: El componente vive en tu proyecto: instálalo con `create-lexy add app-dialog` y edítalo con libertad.\n\n1. **Icono** (opcional, sin fondo, centrado)\n2. **Headline** (título, centrado si hay icono)\n3. **Supporting text** (text-sm, igual que botones)\n4. **Content area** (scrollable si es necesario)\n5. **Action area** (botones, sin divider, alineación configurable)\n\n## Import\n\n```tsx\nimport { AppDialog } from \"@/components/base/AppDialog\";\n```\n\n## Uso básico\n\n```tsx\n<AppDialog\n trigger={<Button variant=\"outline\">Eliminar cuenta</Button>}\n title=\"¿Eliminar cuenta?\"\n description=\"Esta acción no se puede deshacer.\"\n confirmLabel=\"Eliminar\"\n confirmVariant=\"destructive\"\n onConfirm={() => {\n deleteAccount();\n }}\n/>\n```\n\n## Con icono\n\n```tsx\nimport { AlertCircle } from \"lucide-react\";\n\n<AppDialog\n trigger={<Button variant=\"outline\">Eliminar</Button>}\n icon={<AlertCircle className=\"size-6 text-destructive\" />}\n title=\"¿Eliminar cuenta?\"\n description=\"Esta acción no se puede deshacer.\"\n confirmLabel=\"Eliminar\"\n confirmVariant=\"destructive\"\n onConfirm={() => deleteAccount()}\n/>\n```\n\n## Uso controlado\n\n```tsx\nconst [open, setOpen] = useState(false);\n\n<AppDialog\n open={open}\n onOpenChange={setOpen}\n trigger={<Button>Invitar miembro</Button>}\n title=\"Invitar miembro\"\n description=\"Envía una invitación por email.\"\n confirmLabel=\"Invitar\"\n onConfirm={() => {\n sendInvite();\n return true; // cierra el diálogo\n }}\n>\n <Input placeholder=\"Email\" />\n</AppDialog>\n```\n\n## Alineación de acciones\n\n```tsx\n// Centrado para decisiones binarias\n<AppDialog\n trigger={<Button>Confirmar</Button>}\n title=\"¿Estás seguro?\"\n actionsAlignment=\"center\"\n confirmLabel=\"Sí\"\n cancelLabel=\"No\"\n/>\n\n// Space-between para guardar/descartar\n<AppDialog\n trigger={<Button>Salir</Button>}\n title=\"Cambios sin guardar\"\n actionsAlignment=\"space-between\"\n confirmLabel=\"Guardar\"\n cancelLabel=\"Descartar\"\n/>\n```\n\n## Props\n\n| Prop | Tipo | Default | Descripción |\n|---|---|---|---|\n| `trigger` | `ReactNode` | — | Elemento que abre el diálogo. |\n| `icon` | `ReactNode?` | — | Icono arriba del título (sin fondo). |\n| `title` | `string` | — | **Requerido.** Título (headline). |\n| `description` | `string?` | — | Texto de soporte (text-sm). |\n| `children` | `ReactNode?` | — | Contenido principal entre descripción y acciones. |\n| `confirmLabel` | `string` | `\"Confirmar\"` | Texto del botón de acción. |\n| `cancelLabel` | `string` | `\"Cancelar\"` | Texto del botón de cancelar. |\n| `confirmVariant` | `Button variant` | `\"default\"` | Variante del botón de confirmar. |\n| `confirmDisabled` | `boolean` | `false` | Deshabilita el botón de confirmar. |\n| `onConfirm` | `() => boolean \\| void \\| Promise<...>` | — | Si devuelve `false`, no cierra. |\n| `onCancel` | `() => void` | — | Se llama al cancelar o cerrar. |\n| `actionsAlignment` | `\"right\" \\| \"center\" \\| \"left\" \\| \"space-between\"` | `\"right\"` | Alineación de botones. |\n| `open` | `boolean?` | — | Modo controlado. |\n| `onOpenChange` | `(boolean) => void` | — | Callback de cambio de estado. |\n| `hideFooter` | `boolean` | `false` | Oculta el action area. |\n\n## Receta compuesta\n\n`AppDialog` es un wrapper ergonómico sobre el `Dialog` compuesto: las props extra (`aria-*`, `onEscapeKeyDown`, `onInteractOutside`…) pasan al `DialogContent` y el `ref` apunta a ese nodo. Si la estructura canónica no te alcanza (varios bloques de acciones, layout propio, sin título), usa las piezas directamente:\n\n```tsx\nimport { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from \"@/components/base/Dialog\";\n```\n\n## Reglas\n\n- **Sí** usa `icon` para alertas o confirmaciones importantes (icono plano, sin fondo).\n- **Sí** usa `actionsAlignment=\"center\"` para decisiones binarias (sí/no).\n- **Sí** usa `actionsAlignment=\"space-between\"` para guardar/descartar.\n- **No** uses fondo coloreado en el icono; Material 3 usa icono plano.\n- **No** bajes a `Dialog` primitivo si `AppDialog` cubre el caso.\n- El supporting text usa `text-sm` para igualar la tipografía del action area.\n- No hay dividers entre contenido y acciones (siguiendo Material 3).\n- Si hay `icon`, el headline se centra. Si no hay icono, se alinea a la izquierda.\n\n## Para IA\n\n1. Identifica el patrón: ¿es confirmación, formulario modal o alerta informativa?\n2. Si es confirmación/formulario → usa `AppDialog`.\n3. Si es una alerta importante → añade `icon` (sin fondo, icono plano).\n4. Define `title` y `description` (supporting text).\n5. Si hay formulario, pasa inputs como `children` y maneja `onConfirm` para validar.\n6. Elige `actionsAlignment` según el contexto:\n - `right` para acciones estándar\n - `center` para decisiones binarias (sí/no)\n - `space-between` para guardar/descartar\n7. Usa `confirmVariant=\"destructive\"` para acciones irreversibles.\n8. Usa `open` + `onOpenChange` si necesitas controlar el estado desde fuera.\n9. Solo usa `Dialog` primitivo si necesitas un layout sin footer o sin título.\n"
23
+ "content": "# AppDialog — guía de uso\n\nDiálogo de aplicación al estilo **Material 3**. Estructura canónica sin dividers: El componente vive en tu proyecto: instálalo con `create-lexy add app-dialog` y edítalo con libertad.\n\n1. **Icono** (opcional, sin fondo, centrado)\n2. **Headline** (título, centrado si hay icono)\n3. **Supporting text** (text-sm, igual que botones)\n4. **Content area** (scrollable si es necesario)\n5. **Action area** (botones, sin divider, alineación configurable)\n\n## Cuándo usarlo\n\nPara diálogos de aplicación con la estructura ya resuelta: icono, título, descripción y acciones al estilo Material 3. La vía por defecto para modales — pides solo los datos y evitas inconsistencias de composición.\n\n## Composición\n\nUna sola pieza data-driven (`AppDialog`) sobre `Dialog`: recibe `title`, `description`, icono y acciones; el layout canónico (sin dividers) ya viene armado.\n\n## Uso básico\n\n```tsx\n<AppDialog\n trigger={<Button variant=\"outline\">Eliminar cuenta</Button>}\n title=\"¿Eliminar cuenta?\"\n description=\"Esta acción no se puede deshacer.\"\n confirmLabel=\"Eliminar\"\n confirmVariant=\"destructive\"\n onConfirm={() => {\n deleteAccount();\n }}\n/>\n```\n\n## Con icono\n\n```tsx\nimport { AlertCircle } from \"lucide-react\";\n\n<AppDialog\n trigger={<Button variant=\"outline\">Eliminar</Button>}\n icon={<AlertCircle className=\"size-6 text-destructive\" />}\n title=\"¿Eliminar cuenta?\"\n description=\"Esta acción no se puede deshacer.\"\n confirmLabel=\"Eliminar\"\n confirmVariant=\"destructive\"\n onConfirm={() => deleteAccount()}\n/>\n```\n\n## Uso controlado\n\n```tsx\nconst [open, setOpen] = useState(false);\n\n<AppDialog\n open={open}\n onOpenChange={setOpen}\n trigger={<Button>Invitar miembro</Button>}\n title=\"Invitar miembro\"\n description=\"Envía una invitación por email.\"\n confirmLabel=\"Invitar\"\n onConfirm={() => {\n sendInvite();\n return true; // cierra el diálogo\n }}\n>\n <Input placeholder=\"Email\" />\n</AppDialog>\n```\n\n## Alineación de acciones\n\n```tsx\n// Centrado para decisiones binarias\n<AppDialog\n trigger={<Button>Confirmar</Button>}\n title=\"¿Estás seguro?\"\n actionsAlignment=\"center\"\n confirmLabel=\"Sí\"\n cancelLabel=\"No\"\n/>\n\n// Space-between para guardar/descartar\n<AppDialog\n trigger={<Button>Salir</Button>}\n title=\"Cambios sin guardar\"\n actionsAlignment=\"space-between\"\n confirmLabel=\"Guardar\"\n cancelLabel=\"Descartar\"\n/>\n```\n\n## Receta compuesta\n\n`AppDialog` es un wrapper ergonómico sobre el `Dialog` compuesto: las props extra (`aria-*`, `onEscapeKeyDown`, `onInteractOutside`…) pasan al `DialogContent` y el `ref` apunta a ese nodo. Si la estructura canónica no te alcanza (varios bloques de acciones, layout propio, sin título), usa las piezas directamente:\n\n```tsx\nimport { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from \"@/components/base/Dialog\";\n```\n\n## Reglas\n\n- **Sí** usa `icon` para alertas o confirmaciones importantes (icono plano, sin fondo).\n- **Sí** usa `actionsAlignment=\"center\"` para decisiones binarias (sí/no).\n- **Sí** usa `actionsAlignment=\"space-between\"` para guardar/descartar.\n- **No** uses fondo coloreado en el icono; Material 3 usa icono plano.\n- **No** bajes a `Dialog` primitivo si `AppDialog` cubre el caso.\n- El supporting text usa `text-sm` para igualar la tipografía del action area.\n- No hay dividers entre contenido y acciones (siguiendo Material 3).\n- Si hay `icon`, el headline se centra. Si no hay icono, se alinea a la izquierda.\n\n## Import\n\n```tsx\nimport { AppDialog } from \"@/components/base/AppDialog\";\n```\n\n## Props\n\n| Prop | Tipo | Default | Descripción |\n|---|---|---|---|\n| `trigger` | `ReactNode` | — | Elemento que abre el diálogo. |\n| `icon` | `ReactNode?` | — | Icono arriba del título (sin fondo). |\n| `title` | `string` | — | **Requerido.** Título (headline). |\n| `description` | `string?` | — | Texto de soporte (text-sm). |\n| `children` | `ReactNode?` | — | Contenido principal entre descripción y acciones. |\n| `confirmLabel` | `string` | `\"Confirmar\"` | Texto del botón de acción. |\n| `cancelLabel` | `string` | `\"Cancelar\"` | Texto del botón de cancelar. |\n| `confirmVariant` | `Button variant` | `\"default\"` | Variante del botón de confirmar. |\n| `confirmDisabled` | `boolean` | `false` | Deshabilita el botón de confirmar. |\n| `onConfirm` | `() => boolean \\| void \\| Promise<...>` | — | Si devuelve `false`, no cierra. |\n| `onCancel` | `() => void` | — | Se llama al cancelar o cerrar. |\n| `actionsAlignment` | `\"right\" \\| \"center\" \\| \"left\" \\| \"space-between\"` | `\"right\"` | Alineación de botones. |\n| `open` | `boolean?` | — | Modo controlado. |\n| `onOpenChange` | `(boolean) => void` | — | Callback de cambio de estado. |\n| `hideFooter` | `boolean` | `false` | Oculta el action area. |\n\n## Para IA\n\n1. Identifica el patrón: ¿es confirmación, formulario modal o alerta informativa?\n2. Si es confirmación/formulario → usa `AppDialog`.\n3. Si es una alerta importante → añade `icon` (sin fondo, icono plano).\n4. Define `title` y `description` (supporting text).\n5. Si hay formulario, pasa inputs como `children` y maneja `onConfirm` para validar.\n6. Elige `actionsAlignment` según el contexto:\n - `right` para acciones estándar\n - `center` para decisiones binarias (sí/no)\n - `space-between` para guardar/descartar\n7. Usa `confirmVariant=\"destructive\"` para acciones irreversibles.\n8. Usa `open` + `onOpenChange` si necesitas controlar el estado desde fuera.\n9. Solo usa `Dialog` primitivo si necesitas un layout sin footer o sin título.\n"
24
24
  }
25
25
  ]
26
26
  }
@@ -23,7 +23,7 @@
23
23
  "path": "components/base/AppHeaderBar.md",
24
24
  "type": "registry:file",
25
25
  "target": "components/base/AppHeaderBar.md",
26
- "content": "# AppHeaderBar — guía de uso\n\nBarra de navegación superior **data-driven**. Describe la navegación como datos\n(`items`, `actionItems`) y el componente compone internamente las primitivas de\n`HeaderBar`. **Esta es la vía a usar para construir header bars.** No compongas a\nmano `HeaderBar` directamente si `AppHeaderBar` cubre el caso. El componente vive en tu proyecto: instálalo con `create-lexy add app-header-bar` y edítalo con libertad.\n\n## Import\n\n```tsx\nimport { AppHeaderBar } from \"@/components/base/AppHeaderBar\";\n```\n\n## Uso básico\n\n```tsx\n<AppHeaderBar\n items={[\n { label: \"Dashboard\", href: \"/\", active: true },\n { label: \"Proyectos\", href: \"/proyectos\" },\n { label: \"Configuración\", href: \"/config\", disabled: true },\n ]}\n actionItems={[\n { label: \"Nuevo\", icon: Plus, onClick: () => openModal() },\n ]}\n/>\n```\n\n## Uso con marca personalizada\n\n```tsx\n<AppHeaderBar\n brand={<Logo layout=\"horizontal\" />}\n items={[\n { label: \"Inicio\", href: \"/\" },\n { label: \"Productos\", href: \"/productos\" },\n ]}\n actions={<UserMenu />}\n/>\n```\n\n## Sin marca\n\n```tsx\n<AppHeaderBar\n brand={false}\n items={[\n { label: \"Inicio\", href: \"/\" },\n ]}\n/>\n```\n\n## Marca como imagen\n\n```tsx\n<AppHeaderBar\n brand={<img src=\"/logo.png\" alt=\"Mi Marca\" className=\"h-8 w-auto\" />}\n items={[\n { label: \"Inicio\", href: \"/\" },\n ]}\n/>\n```\n\n## Props\n\n### `AppHeaderBar`\n\n| Prop | Tipo | Default | Descripción |\n|---|---|---|---|\n| `brand` | `ReactNode` | — | Marca del producto (por defecto, `Logo`). |\n| `items` | `AppHeaderBarItem[]` | — | Items de navegación principales. |\n| `actions` | `ReactNode` | — | Acciones como ReactNode (a la derecha). |\n| `actionItems` | `AppHeaderBarAction[]` | — | Acciones como datos (alternativa a `actions`). |\n| `onItemClick` | `(item) => void` | — | Callback al hacer click en un item. |\n| `surface` | `\"default\" \\| \"transparent\" \\| \"navy\"` | `\"default\"` | Superficie de fondo. |\n| `bordered` | `boolean` | `true` | Borde inferior. |\n| `padding` | `\"page\" \\| \"none\"` | `\"page\"` | Padding horizontal. |\n| `sticky` | `boolean` | `false` | Fija en la parte superior. |\n\n### `AppHeaderBarItem`\n\n| Campo | Tipo | Descripción |\n|---|---|---|\n| `label` | `string` | Texto visible. |\n| `href` | `string?` | URL de navegación. |\n| `active` | `boolean?` | Marca el item activo. |\n| `disabled` | `boolean?` | No navegable, atenuado. |\n| `onClick` | `() => void` | Callback al hacer click. |\n\n### `AppHeaderBarAction`\n\n| Campo | Tipo | Descripción |\n|---|---|---|\n| `label` | `string` | Texto del botón. |\n| `icon` | `LucideIcon?` | Icono a la izquierda. |\n| `onClick` | `() => void` | Callback al hacer click. |\n| `variant` | `\"default\" \\| \"ghost\" \\| \"outline\"` | Variante del botón. |\n| `disabled` | `boolean?` | Deshabilita el botón. |\n\n## Receta compuesta\n\n`AppHeaderBar` es un wrapper ergonómico sobre `HeaderBar` + `Button`: describe la navegación como datos y compone las piezas en el orden correcto; las props extra pasan al `<header>` y el `ref` apunta a ese nodo. Para estructuras distintas (mega-menús, buscador central), usa `HeaderBar` directamente con `brand`/`actions` como ReactNode.\n\n## Reglas\n\n- Define la navegación como datos y pásala en `items`.\n- Usa `actionItems` para botones de acción a la derecha.\n- Usa `actions` (ReactNode) solo si necesitas componentes complejos (menus, dropdowns).\n- Marca la ruta actual con `active`.\n- Usa `disabled` para opciones bloqueadas en vez de ocultarlas.\n- `surface=\"navy\"` usa el color de marca `#0b013c`.\n- Para casos a medida que no encajen en `items`/`actionItems`, usa `HeaderBar` directamente.\n\n## Para IA\n\n1. Identifica la navegación principal de la página.\n2. Crea `AppHeaderBarItem[]` con los items de navegación.\n3. Define `active` en el item de la ruta actual.\n4. Crea `AppHeaderBarAction[]` para botones de acción (nuevo, crear, etc.).\n5. Usa `onItemClick` si necesitas navegación client-side (React Router, etc.).\n6. Si necesitas menus complejos o dropdowns, usa `actions` (ReactNode) en vez de `actionItems`.\n7. Solo usa `HeaderBar` primitivo si necesitas un layout que no encaje en `items`/`actionItems`.\n"
26
+ "content": "# AppHeaderBar — guía de uso\n\nBarra de navegación superior **data-driven**. Describe la navegación como datos\n(`items`, `actionItems`) y el componente compone internamente las primitivas de\n`HeaderBar`. **Esta es la vía a usar para construir header bars.** No compongas a\nmano `HeaderBar` directamente si `AppHeaderBar` cubre el caso. El componente vive en tu proyecto: instálalo con `create-lexy add app-header-bar` y edítalo con libertad.\n\n## Cuándo usarlo\n\nPara la barra superior descrita como datos: marca, navegación y acciones sin componer piezas a mano. La vía rápida para el header de una app generada.\n\n## Composición\n\nUna sola pieza data-driven (`AppHeaderBar`) que compone `HeaderBar` por debajo: marca (logo o imagen), items de navegación y acciones descritos por props.\n\n## Uso básico\n\n```tsx\n<AppHeaderBar\n items={[\n { label: \"Dashboard\", href: \"/\", active: true },\n { label: \"Proyectos\", href: \"/proyectos\" },\n { label: \"Configuración\", href: \"/config\", disabled: true },\n ]}\n actionItems={[\n { label: \"Nuevo\", icon: Plus, onClick: () => openModal() },\n ]}\n/>\n```\n\n## Uso con marca personalizada\n\n```tsx\n<AppHeaderBar\n brand={<Logo layout=\"horizontal\" />}\n items={[\n { label: \"Inicio\", href: \"/\" },\n { label: \"Productos\", href: \"/productos\" },\n ]}\n actions={<UserMenu />}\n/>\n```\n\n## Sin marca\n\n```tsx\n<AppHeaderBar\n brand={false}\n items={[\n { label: \"Inicio\", href: \"/\" },\n ]}\n/>\n```\n\n## Marca como imagen\n\n```tsx\n<AppHeaderBar\n brand={<img src=\"/logo.png\" alt=\"Mi Marca\" className=\"h-8 w-auto\" />}\n items={[\n { label: \"Inicio\", href: \"/\" },\n ]}\n/>\n```\n\n## Receta compuesta\n\n`AppHeaderBar` es un wrapper ergonómico sobre `HeaderBar` + `Button`: describe la navegación como datos y compone las piezas en el orden correcto; las props extra pasan al `<header>` y el `ref` apunta a ese nodo. Para estructuras distintas (mega-menús, buscador central), usa `HeaderBar` directamente con `brand`/`actions` como ReactNode.\n\n## Reglas\n\n- Define la navegación como datos y pásala en `items`.\n- Usa `actionItems` para botones de acción a la derecha.\n- Usa `actions` (ReactNode) solo si necesitas componentes complejos (menus, dropdowns).\n- Marca la ruta actual con `active`.\n- Usa `disabled` para opciones bloqueadas en vez de ocultarlas.\n- `surface=\"navy\"` usa el color de marca `#0b013c`.\n- Para casos a medida que no encajen en `items`/`actionItems`, usa `HeaderBar` directamente.\n\n## Import\n\n```tsx\nimport { AppHeaderBar } from \"@/components/base/AppHeaderBar\";\n```\n\n## Props\n\n### `AppHeaderBar`\n\n| Prop | Tipo | Default | Descripción |\n|---|---|---|---|\n| `brand` | `ReactNode` | — | Marca del producto (por defecto, `Logo`). |\n| `items` | `AppHeaderBarItem[]` | — | Items de navegación principales. |\n| `actions` | `ReactNode` | — | Acciones como ReactNode (a la derecha). |\n| `actionItems` | `AppHeaderBarAction[]` | — | Acciones como datos (alternativa a `actions`). |\n| `onItemClick` | `(item) => void` | — | Callback al hacer click en un item. |\n| `surface` | `\"default\" \\| \"transparent\" \\| \"navy\"` | `\"default\"` | Superficie de fondo. |\n| `bordered` | `boolean` | `true` | Borde inferior. |\n| `padding` | `\"page\" \\| \"none\"` | `\"page\"` | Padding horizontal. |\n| `sticky` | `boolean` | `false` | Fija en la parte superior. |\n\n### `AppHeaderBarItem`\n\n| Campo | Tipo | Descripción |\n|---|---|---|\n| `label` | `string` | Texto visible. |\n| `href` | `string?` | URL de navegación. |\n| `active` | `boolean?` | Marca el item activo. |\n| `disabled` | `boolean?` | No navegable, atenuado. |\n| `onClick` | `() => void` | Callback al hacer click. |\n\n### `AppHeaderBarAction`\n\n| Campo | Tipo | Descripción |\n|---|---|---|\n| `label` | `string` | Texto del botón. |\n| `icon` | `LucideIcon?` | Icono a la izquierda. |\n| `onClick` | `() => void` | Callback al hacer click. |\n| `variant` | `\"default\" \\| \"ghost\" \\| \"outline\"` | Variante del botón. |\n| `disabled` | `boolean?` | Deshabilita el botón. |\n\n## Para IA\n\n1. Identifica la navegación principal de la página.\n2. Crea `AppHeaderBarItem[]` con los items de navegación.\n3. Define `active` en el item de la ruta actual.\n4. Crea `AppHeaderBarAction[]` para botones de acción (nuevo, crear, etc.).\n5. Usa `onItemClick` si necesitas navegación client-side (React Router, etc.).\n6. Si necesitas menus complejos o dropdowns, usa `actions` (ReactNode) en vez de `actionItems`.\n7. Solo usa `HeaderBar` primitivo si necesitas un layout que no encaje en `items`/`actionItems`.\n"
27
27
  }
28
28
  ]
29
29
  }
@@ -22,7 +22,7 @@
22
22
  "path": "components/base/AppSidebar.md",
23
23
  "type": "registry:file",
24
24
  "target": "components/base/AppSidebar.md",
25
- "content": "# AppSidebar — guía de uso\n\nBarra lateral de navegación **data-driven**. Describes la navegación como datos\n(`groups`, `user`, `logo`) y el componente compone internamente las primitivas\nde `Sidebar`. **Esta es la vía a usar para construir sidebars.** No compongas a\nmano las primitivas de `Sidebar` (`SidebarGroup`, `SidebarMenuButton`, etc.) ni\ninsertes piezas sueltas (`SidebarSeparator`…): `AppSidebar` ya arma la\ncomposición correcta. El componente vive en tu proyecto: instálalo con `create-lexy add app-sidebar` y edítalo con libertad.\n\n## Import\n\n```tsx\nimport { AppSidebar } from \"@/components/base/AppSidebar\";\nimport {\n SidebarProvider,\n SidebarInset,\n SidebarTrigger,\n} from \"@/components/base/Sidebar\";\n```\n\n## Layout canónico\n\n`AppSidebar` vive dentro de `SidebarProvider`, junto a `SidebarInset` para el\ncontenido. El botón de colapso lo aporta `AppSidebar` **dentro** de su cabecera\n(expandida); cuando está colapsada, renderiza un `SidebarTrigger` **fuera** (en\nel contenido) para reabrirla. Solo aparece si `collapsible !== \"none\"`.\n\n```tsx\nimport { useState } from \"react\";\n\nexport function AppLayout() {\n const [open, setOpen] = useState(true);\n\n return (\n <SidebarProvider open={open} onOpenChange={setOpen}>\n <AppSidebar\n collapsible=\"offcanvas\"\n logo={<Logo layout=\"horizontal\" />}\n logoIcon={<Logo layout=\"isotipe\" className=\"size-7\" />}\n groups={groups}\n user={{ name: \"Ada Lovelace\", email: \"ada@lexy.cl\", avatar: \"/avatar.jpg\" }}\n />\n <SidebarInset>\n {!open && (\n <div className=\"p-4\">\n <SidebarTrigger /> {/* reabrir cuando está colapsada */}\n </div>\n )}\n <main>{/* contenido */}</main>\n </SidebarInset>\n </SidebarProvider>\n );\n}\n```\n\n## Modelo de datos\n\n```tsx\nconst groups: AppSidebarGroup[] = [\n {\n // grupo plano (sin título): ítems de primer nivel con icono\n items: [\n { title: \"Dashboard\", url: \"/\", icon: LayoutDashboard, isActive: true },\n { title: \"Proyectos\", url: \"/proyectos\", icon: FolderKanban, badge: 4 },\n {\n title: \"Equipo\",\n icon: Users,\n items: [ // branch → se expande (chevron)\n { title: \"Miembros\", url: \"/equipo/miembros\" },\n { title: \"Roles\", url: \"/equipo/roles\" },\n ],\n },\n ],\n },\n {\n label: \"Análisis\",\n collapsible: true, // grupo colapsable → ítems indentados (un nivel)\n defaultOpen: true,\n items: [\n { title: \"Reportes\", url: \"/reportes\", icon: BarChart3 },\n { title: \"Ventas\", url: \"/ventas\", isActive: true },\n { title: \"Tráfico\", url: \"/trafico\", badge: 12 },\n ],\n },\n];\n```\n\n## Props\n\n### `AppSidebar`\n\n| Prop | Tipo | Default | Descripción |\n|---|---|---|---|\n| `groups` | `AppSidebarGroup[]` | — | **Requerido.** Estructura de navegación. |\n| `logo` | `ReactNode` | — | Marca en la cabecera (expandida). |\n| `logoIcon` | `ReactNode` | — | Marca compacta para el modo iconos (isotipo). |\n| `user` | `{ name, email?, avatar? }` | — | Pie de usuario. Omitir para ocultarlo. |\n| `footer` | `ReactNode` | — | Pie a medida; sobrescribe `user`. |\n| `collapsible` | `\"offcanvas\" \\| \"icon\" \\| \"none\"` | `\"offcanvas\"` | Comportamiento al colapsar. |\n| `side` | `\"left\" \\| \"right\"` | `\"left\"` | Lado de la pantalla. |\n| `variant` | `\"sidebar\" \\| \"floating\" \\| \"inset\"` | `\"sidebar\"` | Estilo del contenedor. |\n| `bordered` | `boolean` | `true` | Borde lateral divisorio. Independiente del colapso. |\n\n### `AppSidebarGroup`\n\n| Campo | Tipo | Descripción |\n|---|---|---|\n| `label` | `string?` | Encabezado del grupo. Si se omite, los ítems van sin título. |\n| `collapsible` | `boolean?` | Encabezado con chevron que despliega/colapsa. |\n| `defaultOpen` | `boolean?` | Estado inicial si es colapsable (default `true`). |\n| `items` | `AppSidebarItem[]` | Ítems del grupo. |\n\n### `AppSidebarItem`\n\n| Campo | Tipo | Descripción |\n|---|---|---|\n| `title` | `string` | Texto visible. |\n| `url` | `string?` | Si se define, se renderiza como `<a href>`. |\n| `icon` | `LucideIcon?` | Icono a la izquierda. |\n| `isActive` | `boolean?` | Marca la página actual. |\n| `disabled` | `boolean?` | No navegable, atenuado. |\n| `badge` | `string \\| number?` | Contador/etiqueta a la derecha. |\n| `items` | `AppSidebarItem[]?` | Subítems (solo en grupo plano → ítem expandible). |\n| `defaultOpen` | `boolean?` | Estado inicial si tiene subítems. |\n\n## Receta compuesta\n\n`AppSidebar` es un wrapper ergonómico sobre las primitivas compuestas de `Sidebar`: describe la navegación como datos (`groups`) y compone la estructura canónica; las props extra pasan al `Sidebar` raíz y el `ref` apunta a ese nodo. Para layouts a medida (secciones custom, búsqueda embebida), usa las primitivas directamente:\n\n```tsx\nimport { Sidebar, SidebarContent, SidebarGroup, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarProvider } from \"@/components/base/Sidebar\";\n```\n\n## Reglas\n\n- Asigna `icon` a **todos** los ítems de primer nivel: en `collapsible=\"icon\"`\n son lo único visible (la etiqueta aparece como tooltip).\n- No anides más de un nivel de subítems.\n- En un grupo colapsable los ítems van planos (un nivel); el anidamiento\n `item.items` aplica a grupos planos.\n- `bordered` controla el borde con independencia del modo de colapso.\n- Para casos a medida que no encajen en `groups`, usa las primitivas de\n `Sidebar` directamente; no mezcles ambos enfoques en la misma barra.\n"
25
+ "content": "# AppSidebar — guía de uso\n\nBarra lateral de navegación **data-driven**. Describes la navegación como datos\n(`groups`, `user`, `logo`) y el componente compone internamente las primitivas\nde `Sidebar`. **Esta es la vía a usar para construir sidebars.** No compongas a\nmano las primitivas de `Sidebar` (`SidebarGroup`, `SidebarMenuButton`, etc.) ni\ninsertes piezas sueltas (`SidebarSeparator`…): `AppSidebar` ya arma la\ncomposición correcta. El componente vive en tu proyecto: instálalo con `create-lexy add app-sidebar` y edítalo con libertad.\n\n## Cuándo usarlo\n\nPara la navegación lateral de una app interna descrita como datos: grupos, items, iconos, badges y estado activo sin componer primitivas a mano. Es la vía por defecto del layout CRM; colapsable y responsive (Sheet en móvil).\n\n## Composición\n\nUna sola pieza data-driven (`AppSidebar`) que arma por debajo las primitivas de `Sidebar`: recibe grupos e items (título, url, icono, badge, subitems) y resuelve colapso, estado activo y variante móvil. El área de trabajo va en `SidebarInset`.\n\n## Layout canónico\n\n`AppSidebar` vive dentro de `SidebarProvider`, junto a `SidebarInset` para el\ncontenido. El botón de colapso lo aporta `AppSidebar` **dentro** de su cabecera\n(expandida); cuando está colapsada, renderiza un `SidebarTrigger` **fuera** (en\nel contenido) para reabrirla. Solo aparece si `collapsible !== \"none\"`.\n\n```tsx\nimport { useState } from \"react\";\n\nexport function AppLayout() {\n const [open, setOpen] = useState(true);\n\n return (\n <SidebarProvider open={open} onOpenChange={setOpen}>\n <AppSidebar\n collapsible=\"offcanvas\"\n logo={<Logo layout=\"horizontal\" />}\n logoIcon={<Logo layout=\"isotipe\" className=\"size-7\" />}\n groups={groups}\n user={{ name: \"Ada Lovelace\", email: \"ada@lexy.cl\", avatar: \"/avatar.jpg\" }}\n />\n <SidebarInset>\n {!open && (\n <div className=\"p-4\">\n <SidebarTrigger /> {/* reabrir cuando está colapsada */}\n </div>\n )}\n <main>{/* contenido */}</main>\n </SidebarInset>\n </SidebarProvider>\n );\n}\n```\n\n## Modelo de datos\n\n```tsx\nconst groups: AppSidebarGroup[] = [\n {\n // grupo plano (sin título): ítems de primer nivel con icono\n items: [\n { title: \"Dashboard\", url: \"/\", icon: LayoutDashboard, isActive: true },\n { title: \"Proyectos\", url: \"/proyectos\", icon: FolderKanban, badge: 4 },\n {\n title: \"Equipo\",\n icon: Users,\n items: [ // branch → se expande (chevron)\n { title: \"Miembros\", url: \"/equipo/miembros\" },\n { title: \"Roles\", url: \"/equipo/roles\" },\n ],\n },\n ],\n },\n {\n label: \"Análisis\",\n collapsible: true, // grupo colapsable → ítems indentados (un nivel)\n defaultOpen: true,\n items: [\n { title: \"Reportes\", url: \"/reportes\", icon: BarChart3 },\n { title: \"Ventas\", url: \"/ventas\", isActive: true },\n { title: \"Tráfico\", url: \"/trafico\", badge: 12 },\n ],\n },\n];\n```\n\n## Receta compuesta\n\n`AppSidebar` es un wrapper ergonómico sobre las primitivas compuestas de `Sidebar`: describe la navegación como datos (`groups`) y compone la estructura canónica; las props extra pasan al `Sidebar` raíz y el `ref` apunta a ese nodo. Para layouts a medida (secciones custom, búsqueda embebida), usa las primitivas directamente:\n\n```tsx\nimport { Sidebar, SidebarContent, SidebarGroup, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarProvider } from \"@/components/base/Sidebar\";\n```\n\n## Reglas\n\n- Asigna `icon` a **todos** los ítems de primer nivel: en `collapsible=\"icon\"`\n son lo único visible (la etiqueta aparece como tooltip).\n- No anides más de un nivel de subítems.\n- En un grupo colapsable los ítems van planos (un nivel); el anidamiento\n `item.items` aplica a grupos planos.\n- `bordered` controla el borde con independencia del modo de colapso.\n- Para casos a medida que no encajen en `groups`, usa las primitivas de\n `Sidebar` directamente; no mezcles ambos enfoques en la misma barra.\n\n## Import\n\n```tsx\nimport { AppSidebar } from \"@/components/base/AppSidebar\";\nimport {\n SidebarProvider,\n SidebarInset,\n SidebarTrigger,\n} from \"@/components/base/Sidebar\";\n```\n\n## Props\n\n### `AppSidebar`\n\n| Prop | Tipo | Default | Descripción |\n|---|---|---|---|\n| `groups` | `AppSidebarGroup[]` | — | **Requerido.** Estructura de navegación. |\n| `logo` | `ReactNode` | — | Marca en la cabecera (expandida). |\n| `logoIcon` | `ReactNode` | — | Marca compacta para el modo iconos (isotipo). |\n| `user` | `{ name, email?, avatar? }` | — | Pie de usuario. Omitir para ocultarlo. |\n| `footer` | `ReactNode` | — | Pie a medida; sobrescribe `user`. |\n| `collapsible` | `\"offcanvas\" \\| \"icon\" \\| \"none\"` | `\"offcanvas\"` | Comportamiento al colapsar. |\n| `side` | `\"left\" \\| \"right\"` | `\"left\"` | Lado de la pantalla. |\n| `variant` | `\"sidebar\" \\| \"floating\" \\| \"inset\"` | `\"sidebar\"` | Estilo del contenedor. |\n| `bordered` | `boolean` | `true` | Borde lateral divisorio. Independiente del colapso. |\n\n### `AppSidebarGroup`\n\n| Campo | Tipo | Descripción |\n|---|---|---|\n| `label` | `string?` | Encabezado del grupo. Si se omite, los ítems van sin título. |\n| `collapsible` | `boolean?` | Encabezado con chevron que despliega/colapsa. |\n| `defaultOpen` | `boolean?` | Estado inicial si es colapsable (default `true`). |\n| `items` | `AppSidebarItem[]` | Ítems del grupo. |\n\n### `AppSidebarItem`\n\n| Campo | Tipo | Descripción |\n|---|---|---|\n| `title` | `string` | Texto visible. |\n| `url` | `string?` | Si se define, se renderiza como `<a href>`. |\n| `icon` | `LucideIcon?` | Icono a la izquierda. |\n| `isActive` | `boolean?` | Marca la página actual. |\n| `disabled` | `boolean?` | No navegable, atenuado. |\n| `badge` | `string \\| number?` | Contador/etiqueta a la derecha. |\n| `items` | `AppSidebarItem[]?` | Subítems (solo en grupo plano → ítem expandible). |\n| `defaultOpen` | `boolean?` | Estado inicial si tiene subítems. |\n"
26
26
  }
27
27
  ]
28
28
  }
@@ -19,7 +19,7 @@
19
19
  "path": "components/base/Avatar.md",
20
20
  "type": "registry:file",
21
21
  "target": "components/base/Avatar.md",
22
- "content": "# Avatar — guía de uso\n\nImagen de perfil con fallback. Para identificación visual. El componente vive en tu proyecto: instálalo con `create-lexy add avatar` y edítalo con libertad.\n\n## Import\n\n```tsx\nimport { Avatar } from \"@/components/base/Avatar\";\n```\n\n## Uso básico\n\n```tsx\n<Avatar />\n```\n\n## Props\n\nConsulta la story en Storybook para ver las props disponibles.\n\n## Reglas\n\n- Usa Avatar 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 Avatar 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
+ "content": "# Avatar — guía de uso\n\nImagen de perfil con fallback. Para identificación visual. El componente vive en tu proyecto: instálalo con `create-lexy add avatar` y edítalo con libertad.\n\n## Cuándo usarlo\n\nPara identificar visualmente a una persona u organización: en headers, filas, comentarios, asignaciones. Con fallback de iniciales siempre — la foto es un privilegio, no una garantía.\n\n## Composición\n\n`Avatar` con `AvatarImage` (la foto) y `AvatarFallback` (iniciales sobre fondo de marca), que aparece si la imagen falla o no existe.\n\n## Uso básico\n\n```tsx\n<Avatar />\n```\n\n## Reglas\n\n- Usa Avatar según el propósito descrito.\n- No abuses de este componente en contexts donde no aplica.\n\n## Import\n\n```tsx\nimport { Avatar } from \"@/components/base/Avatar\";\n```\n\n## Props\n\nConsulta la story en Storybook para ver las props disponibles.\n\n## Para IA\n\n1. Identifica el contexto de uso del componente.\n2. Importa Avatar 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
23
  }
24
24
  ]
25
25
  }
@@ -19,7 +19,7 @@
19
19
  "path": "components/base/Badge.md",
20
20
  "type": "registry:file",
21
21
  "target": "components/base/Badge.md",
22
- "content": "# Badge — guía de uso\n\nEtiqueta de estado o categoría, **no interactiva** (`<span>`). Para marcar estados (\"Activo\", \"Pendiente\") o clasificar. El componente vive en tu proyecto: instálalo con `create-lexy add badge` y edítalo con libertad.\n\n## Import\n\n```tsx\nimport { Badge } from \"@/components/base/Badge\";\n```\n\n## Uso básico\n\n```tsx\n<Badge>Nuevo</Badge>\n<Badge variant=\"secondary\">Borrador</Badge>\n<Badge variant=\"destructive\">Vencido</Badge>\n<Badge variant=\"outline\">Archivado</Badge>\n```\n\n## Props\n\n| Prop | Tipo | Default | Descripción |\n|---|---|---|---|\n| `variant` | `\"default\" \\| \"secondary\" \\| \"destructive\" \\| \"outline\"` | `\"default\"` | Tono visual. |\n| ...resto | `span` props | — | `className`, `aria-*`, etc. |\n\n## Reglas\n\n- El badge siempre lleva texto: no comuniques estado solo por color (accesibilidad).\n- Es informativo, no clicable. Si necesitas que haga algo, usa `Button` o `DropdownMenu`.\n- Mantén el texto corto (1–2 palabras).\n- Para un punto de estado mínimo junto a texto usa `StatusDot`; para contadores numéricos, `CounterBadge`.\n\n## Cuándo NO usar\n\n- **Conteo numérico** (mensajes, ítems) → `CounterBadge`.\n- **Indicador de estado tipo punto** junto a una fila → `StatusDot`.\n- **Etiqueta removible / chip de filtro** → `Tag` (tiene `removable`).\n- **Algo clicable** → `Button`.\n\n## Para IA\n\n1. ¿Es una etiqueta de estado/categoría estática y corta? Entonces `Badge`.\n2. Elige la variante por intención: `destructive` para estados negativos, `secondary`/`outline` para neutros.\n3. Si es número, punto de estado o chip removible, usa `CounterBadge`/`StatusDot`/`Tag`.\n4. No lo uses como botón.\n"
22
+ "content": "# Badge — guía de uso\n\nEtiqueta de estado o categoría, **no interactiva** (`<span>`). Para marcar estados (\"Activo\", \"Pendiente\") o clasificar. El componente vive en tu proyecto: instálalo con `create-lexy add badge` y edítalo con libertad.\n\n## Cuándo usarlo\n\nPara marcar el estado o la clasificación de algo sin acción asociada: “Activo”, “Pendiente”, “Beta”. Es información pura — si al tocarlo pasa algo, es un botón o un filtro, no un badge.\n\n## Composición\n\nUna sola pieza no interactiva (`Badge`, un `<span>`) con variantes de intención. Para conteos numéricos usa `CounterBadge`; para taxonomía, `Tag`.\n\n## Uso básico\n\n```tsx\n<Badge>Nuevo</Badge>\n<Badge variant=\"secondary\">Borrador</Badge>\n<Badge variant=\"destructive\">Vencido</Badge>\n<Badge variant=\"outline\">Archivado</Badge>\n```\n\n## Reglas\n\n- El badge siempre lleva texto: no comuniques estado solo por color (accesibilidad).\n- Es informativo, no clicable. Si necesitas que haga algo, usa `Button` o `DropdownMenu`.\n- Mantén el texto corto (1–2 palabras).\n- Para un punto de estado mínimo junto a texto usa `StatusDot`; para contadores numéricos, `CounterBadge`.\n\n## Cuándo NO usar\n\n- **Conteo numérico** (mensajes, ítems) → `CounterBadge`.\n- **Indicador de estado tipo punto** junto a una fila → `StatusDot`.\n- **Etiqueta removible / chip de filtro** → `Tag` (tiene `removable`).\n- **Algo clicable** → `Button`.\n\n## Import\n\n```tsx\nimport { Badge } from \"@/components/base/Badge\";\n```\n\n## Props\n\n| Prop | Tipo | Default | Descripción |\n|---|---|---|---|\n| `variant` | `\"default\" \\| \"secondary\" \\| \"destructive\" \\| \"outline\"` | `\"default\"` | Tono visual. |\n| ...resto | `span` props | — | `className`, `aria-*`, etc. |\n\n## Para IA\n\n1. ¿Es una etiqueta de estado/categoría estática y corta? Entonces `Badge`.\n2. Elige la variante por intención: `destructive` para estados negativos, `secondary`/`outline` para neutros.\n3. Si es número, punto de estado o chip removible, usa `CounterBadge`/`StatusDot`/`Tag`.\n4. No lo uses como botón.\n"
23
23
  }
24
24
  ]
25
25
  }