feat(design-system): DESIGN_SYSTEM.md + prompts + extract-design command

- frontend/DESIGN_SYSTEM.md: contrato del @fn_library (regla suprema para
  Claude Design y agentes).
- frontend/design_prompts/: 11 plantillas de prompt (onboarding, dashboard,
  crud, detail, settings, auth, error, custom, handoff_integrate) +
  questionnaire numerado para arranque rapido.
- .claude/commands/extract-design.md: workflow de 10 pasos para extraer
  componentes nuevos y mejoras desde exports "standalone" de Claude Design
  al registry, sync al espejo fn-design-system y push a gitea+github.
- .claude/scripts/extract_design_bundle.py: decodificador del bundle
  (base64+gzip en manifest, nombra JSX por heuristica de header).
- .gitignore: ignorar subrepos/*/ (el mirror fn-design-system es repo
  propio con remotes dataforge/fn-design-system + gutierenmanuel/fn-design-system).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-21 20:46:00 +02:00
parent d771c21a46
commit ca07927d38
15 changed files with 1587 additions and 0 deletions
+281
View File
@@ -0,0 +1,281 @@
# /extract-design — Mejorar @fn_library con exports de Claude Design
Eres un agente mejorador del design system. Tu trabajo es analizar un export "standalone" de Claude Design (`sources/frontend_designs/*.html`), identificar componentes nuevos o mejoras sobre `@fn_library`, aplicarlos al registry y propagarlos al espejo público `subrepos/fn-design-system` (GitHub + Gitea).
**Objetivo:** cada diseño exportado debería dejar el registry un poco mejor que antes. Lo que Claude Design inventó para cubrir un hueco hoy → componente reutilizable del registry mañana.
---
## Argumento
`$ARGUMENTS` — ruta al `.html` en `sources/frontend_designs/`. Si no se proporciona:
1. Lista los `.html` bajo `sources/frontend_designs/` ordenados por fecha.
2. Muestra fecha + nombre + tamaño.
3. Pregunta cuál procesar. Default: el más reciente.
---
## PASO 0 — Validar input
```bash
ls -lht sources/frontend_designs/*.html 2>/dev/null
```
Si no existe el fichero, abortar. Si existe, leer las primeras líneas para confirmar que es un export de Claude Design (`__bundler/manifest`, `__bundler/template` en el HTML).
---
## PASO 1 — Decodificar el bundle
Ejecutar el extractor:
```bash
python3 .claude/scripts/extract_design_bundle.py \
"sources/frontend_designs/<NOMBRE>.html" \
"sources/frontend_designs/<NOMBRE>_extracted/"
```
Esperado: directorio con `app.jsx`, `fn_library_emu.jsx`, `charts_emu.jsx`, `data.jsx` + fuentes woff2 + `manifest.json`.
Si falta alguno de los 4 `.jsx` clave, inspeccionar por UUID; puede que Claude Design haya usado estructura distinta. Reportar al usuario.
---
## PASO 2 — Inventariar el diseño
Leer `app.jsx` y listar **todos los componentes React definidos** (funciones que empiezan con mayúscula o usan `function Xxx(`). Categorizar:
### 2a. Componentes del export que YA existen en `@fn_library`
- Grep el barrel: `cat frontend/functions/ui/index.ts | grep "^export"`.
- Para cada componente del export, ver si aparece en el barrel. Registrar coincidencias.
### 2b. Componentes nuevos (no existen en el registry)
Componentes React del `app.jsx` cuyo nombre no aparece en el barrel. Estos son **candidatos a extracción**.
### 2c. Uso de variantes / props no documentadas
Leer `fn_library_emu.jsx` del export y comparar API con tus `.tsx` reales:
```bash
# Comprobar componentes específicos si el export los usa con props nuevas
sqlite3 registry.db "SELECT id, signature, props FROM functions WHERE id = 'alert_ts_ui';"
```
Anotar discrepancias (variantes faltantes, props nuevas, tipos distintos).
### 2d. Datos/patrones reutilizables en `data.jsx`
- RNG determinista (mulberry32) → candidato a `frontend/functions/core/rng_seeded_ts_core` o `python/functions/core/`.
- Helpers tipo `statusBadge()` → documentar como receta, no como componente.
---
## PASO 3 — Consultar el registry para evitar duplicados
Para cada componente candidato del paso 2b, búsqueda FTS5 antes de proponerlo:
```bash
sqlite3 registry.db "SELECT id, kind, description FROM functions WHERE id IN (SELECT id FROM functions_fts WHERE functions_fts MATCH 'name:<CANDIDATO>* OR description:<PALABRAS_CLAVE>') ORDER BY name;"
```
Si encuentras algo similar que pueda ser mejorado en lugar de duplicado, márcalo como **mejora** a ese existente.
---
## PASO 4 — Presentar el diagnóstico al usuario
Muestra en tablas separadas:
### 🟢 Componentes nuevos candidatos
| # | Nombre propuesto | Dominio | Líneas | Reutilizable en | API |
|---|---|---|---|---|---|
| 1 | `funnel_chart_ts_ui` | ui | ~35 | CRM, analytics, funnels genéricos | `(data: Array<{stage, value}>, variant?) → JSX` |
### 🟡 Mejoras a componentes existentes
| # | Componente | Mejora | Tipo | Riesgo |
|---|---|---|---|---|
| A | `alert_ts_ui` | Añadir variantes `success`, `warning`, `info` | Expandir enum | Bajo — no rompe API |
| B | `data_table_ts_ui` | Prop `density: 'compact'|'cozy'|'roomy'` | Añadir prop opcional | Bajo |
### 🔵 Patrones a documentar (no componente)
| Patrón | Dónde registrar |
|---|---|
| `statusBadge` helper | `DESIGN_SYSTEM.md` sección "patterns" |
**Esperar confirmación.** El usuario responde con sintaxis `1,2,A,B` (o `all`, o `nuevos only`, o descarta algunos). Si dice `all`, aplica todo lo listado.
---
## PASO 5 — Aplicar mejoras aprobadas
### 5a. Para componentes nuevos (candidatos 🟢)
Por cada aprobado:
1. **Leer código** del `app.jsx` / `fn_library_emu.jsx` / `charts_emu.jsx` del export.
2. **Adaptar al stack real del registry:**
- Cambiar elementos SVG/HTML planos por primitivas de `@mantine/core` cuando corresponda (`Paper`, `Stack`, `Group`, `Text`).
- Cambiar `style={{...}}` por props Mantine (`p`, `m`, `fw`, `gap`, `radius`, `c`).
- Si es un chart, delegar en `@mantine/charts` cuando sea posible; solo usar SVG puro si Mantine no cubre el caso (ej: `Sparkline` en el registry ya es SVG puro por rendimiento).
- Iconos: `@tabler/icons-react`.
3. **Crear los dos ficheros** siguiendo la convención:
- `frontend/functions/ui/<name>.tsx` — código React.
- `frontend/functions/ui/<name>.md` — frontmatter completo.
4. **Frontmatter del .md** (campos clave):
```yaml
id: <name>_ts_ui
name: <name>
kind: component
lang: ts
domain: ui
purity: impure
framework: react
version: 1.0.0
description: "..."
tags: [...]
props: {...}
emits: null
params: []
output: "JSX.Element — ..."
source_repo: "claude.ai/design"
source_license: ""
source_file: "sources/frontend_designs/<NOMBRE>.html"
file_path: frontend/functions/ui/<name>.tsx
tested: false
```
5. **Añadir al barrel** `frontend/functions/ui/index.ts`: `export { Xxx } from './<name>'`.
### 5b. Para mejoras a componentes existentes (🟡)
Por cada aprobada:
1. **Leer** el `.tsx` actual.
2. **Aplicar la mejora** sin romper la API existente: añade prop opcional, amplía enum de `variant`, etc.
3. **Actualizar** el `.md` correspondiente para reflejar las nuevas variantes/props (campos `variant`, `props`, `description`).
4. **Si la firma cambia**, actualizar también el `signature` del frontmatter.
### 5c. Para patrones a documentar (🔵)
1. Añadir una sección "Patterns" en `frontend/DESIGN_SYSTEM.md` si no existe.
2. Registrar el patrón con un ejemplo corto.
---
## PASO 6 — Indexar y verificar
```bash
./fn index
```
- Si falla por integridad, arreglar y reintentar.
- Verificar cada componente nuevo: `./fn show <id>`.
- Confirmar que el barrel compila haciendo `cd frontend && pnpm tsc --noEmit` (si tarda, al menos verificar imports manualmente).
---
## PASO 7 — Sincronizar al espejo
```bash
cd subrepos/fn-design-system
./sync_from_registry.sh
git add -A
git status --short # Mostrar qué cambió en el espejo
```
Si hay cambios, preparar commit. Si no, el sync no recogió las modificaciones — investigar.
---
## PASO 8 — Commit en ambos repos
### 8a. Commit en `fn_registry`
```bash
git add frontend/functions/ui/ frontend/DESIGN_SYSTEM.md registry.db
git commit -m "$(cat <<'EOF'
feat(ui): extract <N> components / <M> improvements from design export
From: sources/frontend_designs/<NOMBRE>.html
New components:
- <id> — <descripción corta>
- ...
Improvements:
- <id> — <cambio>
- ...
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
```
### 8b. Commit en el espejo
```bash
cd subrepos/fn-design-system
git commit -m "sync: <N> new components + <M> improvements from <NOMBRE>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>"
```
---
## PASO 9 — Push
### 9a. Push del espejo (ambos remotes)
```bash
cd subrepos/fn-design-system
./push_all.sh
```
Esto propaga a:
- `gitea/dataforge/fn-design-system`
- `github/gutierenmanuel/fn-design-system` ← este es el que Claude Design consume
### 9b. Push de fn_registry
**Preguntar al usuario** antes — no push sin permiso (ver CLAUDE.md del proyecto). Si dice sí:
```bash
git push origin master
```
---
## PASO 10 — Resumen final
Mostrar al usuario:
```
✓ Extracción completa.
Nuevos componentes en @fn_library:
- <id> (frontend/functions/ui/<name>.tsx)
- ...
Mejoras aplicadas:
- <id>: <qué cambió>
Espejo actualizado:
- Commit gitea: <sha> → <url>
- Commit github: <sha> → <url>
Claude Design verá estas mejoras en su próxima lectura del repo enlazado.
Siguiente acción sugerida: probar un prompt de dashboard que use <componente_nuevo>.
```
---
## Reglas críticas
- **NUNCA extraer sin aprobación explícita del usuario** — siempre paso 4 con tabla y espera.
- **NUNCA sobrescribir un componente existente** en el paso 5b — solo añadir variantes/props opcionales. Si la mejora es incompatible, proponerlo como propuesta aparte (`fn proposal add`) en vez de aplicarla.
- **SIEMPRE `source_repo: "claude.ai/design"`** en el frontmatter de componentes nuevos, y `source_file` apuntando al `.html` original.
- **SIEMPRE mantener el orden:** registry → index → verify → sync mirror → commit both → push mirror → (ask to push fn_registry).
- **El barrel `index.ts`** debe estar actualizado antes de hacer `fn index` (hay apps que lo importan).
- **NO committear** `operations.db*`, `node_modules/`, `dist/`, `.env` ni nada que `.gitignore` excluya. Usa `git add` con rutas explícitas, no `git add -A` a ciegas.
- **Si el usuario cancela a mitad**, dejar el working tree limpio o documentar qué quedó pendiente. No medio-commits.
- **Patrones que no tienen sentido como primitiva** (ej. envs, branding específico) → documentar, no componentizar.
+159
View File
@@ -0,0 +1,159 @@
#!/usr/bin/env python3
"""
Extract Claude Design "standalone" HTML exports.
Claude Design packs the whole React app as base64+gzip blobs inside
<script type="__bundler/manifest"> tags. This script decompresses them
and writes each asset (JSX, CSS, fonts) to a target directory.
Usage:
python3 extract_design_bundle.py <path/to/export.html> <output_dir>
The output dir will contain:
data.jsx (if detected by header comment)
fn_library_emu.jsx (lib emulation)
charts_emu.jsx (charts emulation)
app.jsx (main tree)
<uuid>.<ext> (anything else — fonts, unknown js)
manifest.json (summary of all assets: uuid, mime, bytes, filename)
JSX files are named heuristically from their leading comment. If names
cannot be inferred from headers, they keep their uuid prefix.
"""
from __future__ import annotations
import base64
import gzip
import json
import pathlib
import re
import sys
MIME_TO_EXT = {
"text/javascript": "js",
"application/javascript": "js",
"text/babel": "jsx",
"application/json": "json",
"text/css": "css",
"image/svg+xml": "svg",
"font/woff2": "woff2",
"font/woff": "woff",
"text/html": "html",
}
# Order matters: first matching hint wins. Put MORE SPECIFIC patterns first.
HEADER_HINTS = [
("charts_emu.jsx", [r"Emulaci(ó|o)n de @fn_library/\{", r"LineChart, AreaChart, BarChart"]),
("fn_library_emu.jsx", [r"Emulaci(ó|o)n visual de @fn_library"]),
("data.jsx", [r"mock data \(determinista\)", r"window\.\w+Data\s*="]),
("app.jsx", [r"ReactDOM\.createRoot", r"arbol principal", r"function App\s*\("]),
]
def pick_name(content: str, used_names: set[str]) -> str | None:
head = content[:2000]
for name, patterns in HEADER_HINTS:
if name in used_names:
continue
if any(re.search(p, head, re.IGNORECASE) for p in patterns):
return name
return None
def grab_script(html: str, kind: str) -> str | None:
m = re.search(
r'<script type="__bundler/' + kind + r'">\s*(.*?)\s*</script>',
html, re.DOTALL,
)
return m.group(1) if m else None
def extract(html_path: pathlib.Path, out_dir: pathlib.Path) -> dict:
html = html_path.read_text(encoding="utf-8")
manifest_raw = grab_script(html, "manifest")
if not manifest_raw:
raise SystemExit(f"No <script type='__bundler/manifest'> found in {html_path}")
manifest = json.loads(manifest_raw)
ext_raw = grab_script(html, "ext_resources")
ext_resources = json.loads(ext_raw) if ext_raw else []
id_map = {e["uuid"]: e.get("id", e["uuid"]) for e in ext_resources}
out_dir.mkdir(parents=True, exist_ok=True)
summary = []
used_names: set[str] = set()
# First pass: decode all assets and collect jsx blobs (so we can name them by header hint)
decoded: list[tuple[str, str, bytes]] = [] # (uuid, mime, bytes)
for uuid, entry in manifest.items():
raw = base64.b64decode(entry["data"])
if entry.get("compressed"):
raw = gzip.decompress(raw)
decoded.append((uuid, entry.get("mime", "application/octet-stream"), raw))
# Second pass: write files with heuristic names for known jsx
for uuid, mime, raw in decoded:
ext = MIME_TO_EXT.get(mime, "bin")
filename = None
# Heuristic for JSX / JS that represents the app
if ext in ("jsx", "js"):
try:
text = raw.decode("utf-8", errors="replace")
name = pick_name(text, used_names)
if name:
filename = name
used_names.add(name)
except Exception:
pass
if not filename:
# Fall back to ext_resources id if present, or uuid
base = id_map.get(uuid, uuid)
safe = re.sub(r"[^A-Za-z0-9._-]", "_", base)[:80]
filename = f"{safe}.{ext}"
path = out_dir / filename
# Avoid collisions
i = 2
while path.exists():
stem = path.stem
path = out_dir / f"{stem}_{i}.{ext}"
i += 1
path.write_bytes(raw)
summary.append({
"uuid": uuid,
"mime": mime,
"bytes": len(raw),
"filename": path.name,
})
(out_dir / "manifest.json").write_text(
json.dumps({"source": str(html_path), "assets": summary}, indent=2),
encoding="utf-8",
)
return {"assets": summary, "out": str(out_dir)}
def main():
if len(sys.argv) < 3:
print(__doc__)
sys.exit(2)
html = pathlib.Path(sys.argv[1])
out = pathlib.Path(sys.argv[2])
if not html.exists():
sys.exit(f"Input not found: {html}")
result = extract(html, out)
print(f"✓ Extracted {len(result['assets'])} assets to {result['out']}")
print(f" Manifest: {out}/manifest.json")
print()
# Short preview per asset
for a in result["assets"]:
print(f" {a['mime']:28s} {a['bytes']:>8} B {a['filename']}")
if __name__ == "__main__":
main()
+3
View File
@@ -50,6 +50,9 @@ vaults/*/
# Sources — repos externos clonados (solo se versiona el manifest)
sources/*/
# Subrepos — mirrors/espejos externos (cada uno su propio git remote)
subrepos/*/
# External — symlinks a repos ajenos (ej: repo_Claude con skills/commands)
external/
+457
View File
@@ -0,0 +1,457 @@
# @fn_library — Design System
> **Fuente de verdad para Claude Design y para cualquier agente que genere UI en este repo.**
> Si generas código frontend, obedece este documento. Sin excepciones.
---
## 1. Qué es esto
`@fn_library` es el design system del registry. Son 75 wrappers React sobre **Mantine v9** que viven en `frontend/functions/ui/`. Toda app del registry (`apps/*/frontend/`) los consume vía el alias Vite `@fn_library` — barrel único en `frontend/functions/ui/index.ts`.
**Regla suprema:** en las apps NUNCA se importa Mantine directamente, NUNCA se mezclan otras librerías de UI, NUNCA se escribe CSS manual. Todo pasa por `@fn_library`.
---
## 2. Stack
| Capa | Librería | Notas |
|------|----------|-------|
| Framework | React 19 | con TypeScript 6 |
| UI base | `@mantine/core` v9 | todos los wrappers la usan |
| Charts | `@mantine/charts` v9 | line, bar, area, pie — Recharts por debajo |
| Fechas | `@mantine/dates` v9 + `dayjs` | DatePickerInput |
| Forms | `@mantine/form` v9 | validación, no es obligatorio |
| Dropzone | `@mantine/dropzone` v9 | drag & drop de ficheros |
| Hooks | `@mantine/hooks` v9 | disclosure, useForm, etc. |
| Notifs | `@mantine/notifications` v9 | incluidas en FnMantineProvider |
| Iconos | `@tabler/icons-react` v3 | el set nativo de Mantine |
| Fuente | `@fontsource-variable/geist` | cargada en el provider |
| Grafos | `sigma` + `graphology` | solo para `GraphContainer` |
| Build | Vite 8 + pnpm | no webpack, no npm, no yarn |
### Deny-list (NO usar nunca)
-`tailwindcss`, `postcss-preset-tailwind`
-`class-variance-authority` (CVA), `clsx`, `cn`
-`shadcn/ui`, `@radix-ui/*` directos, `@headlessui/*`
-`lucide-react`, `heroicons`, `feather`, `phosphor-icons`
-`chakra-ui`, `mui`, `antd`
-`framer-motion` (Mantine trae transiciones suficientes)
-`styled-components`, `emotion`
- ❌ CSS modules, CSS manual (`.css` inline), `style={{...}}` para maquetar
Si una idea solo encaja con algo de la deny-list → pregunta antes de escribir código.
---
## 3. Import rules
### 3.1 Un solo barrel
```ts
// ✅ Correcto — siempre desde el barrel
import { Button, Card, CardHeader, CardTitle, KPICard, LineChart, DataTable } from '@fn_library'
// ❌ Prohibido — subpath
import { Button } from '@fn_library/button'
// ❌ Prohibido — Mantine directo en apps
import { Button } from '@mantine/core'
```
### 3.2 Provider raíz obligatorio
Toda app monta su árbol dentro de `FnMantineProvider`. No `MantineProvider` directo.
```tsx
import { FnMantineProvider } from '@fn_library'
import { createTheme } from '@mantine/core'
const theme = createTheme({
primaryColor: 'indigo',
fontFamily: 'Geist Variable, sans-serif',
})
export function App() {
return (
<FnMantineProvider theme={theme} defaultColorScheme="dark">
<MyPage />
</FnMantineProvider>
)
}
```
`FnMantineProvider` ya incluye:
- `<Notifications position="top-right" />`
- Los CSS de `@mantine/core`, `@mantine/charts`, `@mantine/notifications`
- `defaultColorScheme="dark"` por defecto
### 3.3 Iconos
```tsx
import { IconPlus, IconTrash, IconRefresh, IconSearch } from '@tabler/icons-react'
<Button leftSection={<IconPlus size={16} />}>Añadir</Button>
```
### 3.4 Layout: solo componentes Mantine de layout
Los componentes de layout (`AppShell`, `Group`, `Stack`, `Grid`, `SimpleGrid`, `Flex`, `Container`, `Box`, `Paper`) **sí** se importan directo de `@mantine/core`, porque `@fn_library` no los wrappea — son parte fija del sistema.
```tsx
import { Stack, Group, SimpleGrid, Paper, Box } from '@mantine/core'
```
El único layout wrappeado es `FnAppShell` (en `@fn_library`) — úsalo para el shell principal de la app.
---
## 4. Reglas duras (obedecer siempre)
1. **Props Mantine, no clases.** Tamaños, márgenes, padding, gap, radio, color: `size`, `p`, `m`, `mt`, `mb`, `gap`, `radius`, `shadow`, `c`, `fw`, `fz`, `lh`. Sin `className="..."` con clases propias.
2. **Color scheme dark** es el default del registro. Los componentes están pensados para oscuro; si quieres claro, pásalo explícito.
3. **No hardcodees colores**. Usa los tokens Mantine (`--mantine-color-*`) o los helpers del registry (`get_series_color_ts_core`, `chart_colors_ts_core`).
4. **Formulario**: envuelve cada campo con `<FormField label="..." error="..." helperText="...">`. Gestiona ARIA, label, error.
5. **Tablas**: usa `DataTable`. Auto-detecta columnas del primer row. No escribas `<table>` a mano.
6. **KPIs**: usa `KPICard` (no Paper manual). Acepta `label`, `value`, `unit`, `delta`, `icon`, `chart`, `action`, `subtitle`.
7. **Charts**: siempre con `@fn_library` charts; nunca Recharts directo. Envuelve en `ChartContainer` si necesitas header/footer del propio chart.
8. **Loading**: `Skeleton` + variantes (`SkeletonText`, `SkeletonCard`, `SkeletonTable`, `SkeletonAvatar`, `SkeletonButton`) o `FnLoadingOverlay` para secciones completas.
9. **Vacío**: `EmptyState` con icono Tabler, título, descripción, acción opcional.
10. **Errores de página entera**: `ErrorPage`. 404/500/403 o código custom.
11. **Modal**: `Dialog`. Drawer lateral: `Sheet`. Popover flotante: `Popover`. Menús: `DropdownMenu`.
12. **Notificaciones**: desde `@mantine/notifications` `notifications.show({...})` — el provider ya está montado. O usa el `Toast` del registry si necesitas la API `useToast`.
---
## 5. Catálogo completo (75 componentes)
> Todos exportados desde `@fn_library` (barrel). Cada uno detalla variantes y nota clave.
### 5.1 Primitives
| Componente | Variantes | Notas |
|------------|-----------|-------|
| `Button` | `default \| outline \| secondary \| ghost \| destructive \| link` — sizes `default \| xs \| sm \| lg \| icon \| icon-xs \| icon-sm \| icon-lg` | Wrapper Mantine Button. Soporta `leftSection`/`rightSection` con icono. |
| `FnActionIcon` | `filled \| light \| outline \| transparent \| default \| subtle` | Botón cuadrado para icono único. Loading + tooltip integrados. |
| `Badge` | 10 semánticas (`default, secondary, destructive, outline, ghost, link, success, warning, error, info`) | 2 tamaños. |
| `Chip` | `filled \| outline \| light` | Seleccionable. `ChipGroup` para selección simple/múltiple. |
| `Indicator` (FnIndicator) | — | Badge posicionado sobre un hijo. |
| `Label` | — | Etiqueta de formulario accesible. |
| `Skeleton` | `base \| text \| card \| avatar \| button \| table` | Exporta también `SkeletonText`, `SkeletonCard`, `SkeletonTable`, `SkeletonAvatar`, `SkeletonButton`. |
| `Sparkline` | `line \| area \| bar` | Mini chart SVG puro — para tablas y KPIs. |
| `ProgressBar` | `primary \| success \| warning \| destructive` | Con buffer, indeterminado y display de valor. |
| `FnRingProgress` | — | Anillo de progreso con secciones. |
| `Tooltip` | `default` | Delay configurable. |
| `Avatar` | `xs \| sm \| md \| lg \| xl` | Fallback a iniciales. |
### 5.2 Inputs
| Componente | Notas |
|------------|-------|
| `Input` | `TextInput` con `InputGroup` e `InputIcon`. |
| `Textarea` | autosize. |
| `PasswordInput` | Toggle visibilidad + strength meter opcional. |
| `NumberInput` (FnNumberInput) | min/max, step, prefix, suffix. |
| `Select` | Declarativo `data={[...]}`. Búsqueda, grupos. |
| `SimpleSelect` | Acepta array plano o agrupado. |
| `MultiSelect` | Pills + límite. |
| `Autocomplete` | Admite valores libres (a diferencia de Select). |
| `TagsInput` | Tags libres. |
| `DatePickerInput` | Fecha simple, múltiple o rango. |
| `FileInput` | Ficheros único/múltiple, tipos MIME. |
| `Dropzone` | Drag & drop de archivos. Estados idle/accept/reject. |
| `Checkbox` | Con indeterminate. |
| `RadioGroup` | Opciones exclusivas. |
| `SwitchToggle` | Label a izquierda o derecha. |
| `FnSegmentedControl` | Selección única entre opciones. |
| `Slider` / `RangeSlider` | Marcas, labels. |
| `Rating` | Fracciones, símbolos custom. |
| `ColorInput` | Picker + swatches + alpha. |
| `PinInput` | Código OTP con autocompletado. |
| `SearchBar` | Input con debounce, icono y clear. |
| `Command` / `CommandSearch` | Combobox cmdk-style con búsqueda, grupos, shortcuts. |
| `FormField` | **Wrapper obligatorio** para cada campo de form. |
### 5.3 Data display
| Componente | Notas |
|------------|-------|
| `DataTable` | Sticky header, overflow scroll, heatmap, formato condicional (number/datetime/currency), auto-columns. Variantes: `default \| heatmap`. |
| `KPICard` | Size `sm \| default \| lg`. Acepta icon, chart inline, action. |
| `Card` + slots | `CardHeader`, `CardTitle`, `CardDescription`, `CardAction`, `CardContent`, `CardFooter`. Variantes `default \| borderless \| ghost`, sizes `default \| sm`. |
| `FnTimeline` | Items con iconos y colores. |
| `EmptyState` | Icono + título + descripción + acción. |
| `Pagination` | Autocontenido. |
### 5.4 Charts
| Componente | Variantes | Notas |
|------------|-----------|-------|
| `LineChart` | 5 curvas (`linear, monotone, step, stepBefore, stepAfter`) | Multi-series, reference lines. |
| `BarChart` | `vertical \| horizontal` | Multi-series. |
| `AreaChart` | `default \| stacked` | Gradientes auto. |
| `PieChart` | `pie \| donut` | Colores auto. |
| `Sparkline` | `line \| area \| bar` | Mini chart inline sin Recharts. |
| `ChartContainer` | `default` | Wrapper Paper + helpers (`getSeriesColor`, `Series`). |
| `GraphContainer` | — | sigma.js + graphology + ForceAtlas2. |
### 5.5 Overlays y feedback
| Componente | Notas |
|------------|-------|
| `Dialog` | Modal con slots (`DialogHeader`, `DialogTitle`, `DialogDescription`, `DialogContent`, `DialogFooter`, `DialogClose`). |
| `Sheet` | Drawer `top \| bottom \| left \| right`. |
| `Popover` | Contenido flotante. |
| `DropdownMenu` | Items, checkboxes, radios, separadores, submenús. |
| `Alert` | `default \| destructive`. Slots `AlertTitle`, `AlertDescription`. |
| `Toast` | `default \| success \| error \| warning \| info`. API `useToast`. |
| `FnLoadingOverlay` | Blur + opacidad. Para secciones o página entera. |
### 5.6 Navegación y shell
| Componente | Notas |
|------------|-------|
| `FnAppShell` | Shell con header + navbar colapsable + main. |
| `PageHeader` | `full \| simple`. Título, subtítulo, acciones, back, tabs integrados, badge, sticky. |
| `Breadcrumb` | Con elipsis y router links vía `asChild`. |
| `FnNavLink` | Link con icono, descripción, anidamiento. |
| `Tabs` | `default \| line`. Horizontal/vertical. |
| `Accordion` | `AccordionItem` + `AccordionTrigger` + `AccordionContent`. |
| `FnStepper` | Horizontal/vertical. |
### 5.7 Hooks y providers (TS)
| Hook / Provider | Fuente | Notas |
|---|---|---|
| `FnMantineProvider` | `@fn_library` | Provider raíz. |
| `useToast` | `@fn_library` (toast) | API imperativa para notificaciones. |
| `useAnimatedCanvas` | `@fn_library` | Canvas con requestAnimationFrame, DPR, FPS real. |
| `WailsProvider` + `useWailsContext` + `useWailsCache` | `@fn_library` | Solo si la app es Wails. |
| `useWailsQuery` / `useWailsMutation` / `useWailsStream` / `useWailsEvent` | `@fn_library` | IPC Wails. |
### 5.8 Hooks TS core (registry TS core, no UI)
No están en `@fn_library` pero son parte del stack. Se importan desde `frontend/functions/core/` o se copian al proyecto según se necesite:
- `use_fetch`, `use_mutation`, `use_sse`, `use_websocket`, `use_infinite_scroll`, `use_debounced_search`, `use_form`
- `api_client`, `http_cache`, `format_compact` (K/M/B, bytes, duración, Hz), `chart_colors`, `get_series_color`, `get_computed_color`
---
## 6. Page generators (preferir antes que montar a mano)
Cuando generes una pantalla, intenta **primero** con un page generator. Solo si no encaja, compone con Card/Paper/SimpleGrid.
| Generator | Cuándo usarlo | Props clave |
|-----------|---------------|-------------|
| `analyticsPage` | Dashboard con KPIs + charts en grid | `title, subtitle, metrics[], charts[], dateRange?, actions?` |
| `crudPage<T>` | Listado + tabla + modal create/edit/delete | `title, columns, rows, schema, onAdd, onEdit, onDelete` |
| `dashboardLayout` | Grid responsive 1-4 cols de widgets con span | `widgets[{content, span}]` |
| `detailPage` | Ficha de entidad con header, campos, tabs, timeline | `entity, fields[], tabs[], timeline[]` |
| `settingsPage` | Config con navegación lateral y secciones | `sections[{nav, fields[]}]` |
| `AuthForm` | Login / register con social buttons | `mode, onSubmit, extraFields?, socialProviders?` |
| `ErrorPage` | 404 / 500 / 403 / custom | `code, title, description, actions` |
Estas funciones viven en `frontend/functions/ui/` igual que los componentes, se importan desde `@fn_library` y retornan un `ReactElement`.
---
## 7. Ejemplos canónicos
### 7.1 Dashboard de analytics
```tsx
import { analyticsPage, LineChart, BarChart, AreaChart, PieChart, Button } from '@fn_library'
import { IconRefresh } from '@tabler/icons-react'
const runsData = [
{ hour: '00', success: 120, failure: 3 },
{ hour: '01', success: 115, failure: 1 },
// ...
]
export const PipelineMonitoring = () => analyticsPage({
title: 'Pipeline Monitoring',
subtitle: 'últimas 24h',
actions: <Button variant="outline" leftSection={<IconRefresh size={14} />}>Refresh</Button>,
metrics: [
{ label: 'Runs totales', value: '1,284', delta: { value: 12, isPositive: true } },
{ label: 'Success rate', value: '97.3%', delta: { value: 0.4, isPositive: true } },
{ label: 'P95 latency', value: '842ms', delta: { value: -8, isPositive: true } },
{ label: 'Errores', value: 34, delta: { value: 5, isPositive: false } },
],
charts: [
{ id: 'runs', title: 'Runs por hora', type: 'line',
content: <LineChart data={runsData} xKey="hour"
series={[{ key: 'success', name: 'Success' }, { key: 'failure', name: 'Failure' }]} /> },
{ id: 'top', title: 'Top pipelines', type: 'bar',
content: <BarChart data={[]} xKey="name" yKey="count" /> },
{ id: 'latency', title: 'Latencia por etapa', type: 'area', span: 2,
content: <AreaChart data={[]} xKey="t" series={[{ key: 'p50' }, { key: 'p95' }]} variant="stacked" /> },
],
})
```
### 7.2 CRUD
```tsx
import { crudPage, Badge } from '@fn_library'
import type { ColumnDef } from '@fn_library'
interface Target { app: string; host: string; port: number; status: 'ok' | 'down' }
const columns: ColumnDef<Target>[] = [
{ key: 'app', label: 'App' },
{ key: 'host', label: 'Host', render: (v) => <Badge variant="outline">{v as string}</Badge> },
{ key: 'port', label: 'Port', format: 'number' },
{ key: 'status', label: 'Status',
render: (v) => <Badge variant={v === 'ok' ? 'success' : 'destructive'}>{v as string}</Badge> },
]
export const DeployTargets = () => crudPage<Target>({
title: 'Deploy Targets',
columns,
rows: [],
schema: {
app: { type: 'text', label: 'App', required: true },
host: { type: 'select', label: 'Host', options: ['vps-1','vps-2'] },
port: { type: 'number', label: 'Port', min: 1, max: 65535 },
build: { type: 'textarea', label: 'Build cmd' },
},
onAdd: async (row) => { /* ... */ },
onEdit: async (row) => { /* ... */ },
onDelete: async (row) => { /* ... */ },
})
```
### 7.3 Página de detalle
```tsx
import { detailPage, Badge } from '@fn_library'
export const FunctionDetail = (fn) => detailPage({
entity: { title: fn.id, subtitle: fn.description, avatar: fn.name.slice(0,2).toUpperCase(),
badge: <Badge variant="secondary">{fn.domain}</Badge>, backHref: '/functions' },
fields: [
{ label: 'Language', value: fn.lang },
{ label: 'Purity', value: fn.purity },
{ label: 'Kind', value: fn.kind },
{ label: 'Version', value: fn.version },
{ label: 'Tested', value: fn.tested ? 'yes' : 'no' },
{ label: 'Source', value: fn.source_repo || '—' },
],
tabs: [
{ key: 'code', label: 'Code', count: 1, content: <pre>{fn.code}</pre> },
{ key: 'tests', label: 'Tests', count: fn.tests_count },
{ key: 'deps', label: 'Deps', count: fn.deps_count },
],
timeline: fn.activity,
})
```
---
## 8. Theming
Cada app define su `theme`:
```tsx
import { createTheme, type MantineThemeOverride } from '@mantine/core'
export const theme: MantineThemeOverride = createTheme({
primaryColor: 'indigo',
primaryShade: { light: 6, dark: 4 },
fontFamily: 'Geist Variable, sans-serif',
headings: { fontFamily: 'Geist Variable, sans-serif', fontWeight: '600' },
defaultRadius: 'md',
// colores custom solo si son parte del brand
colors: {
brand: ['#f0f4ff','#d9e2ff','#b3c7ff','#8daaff','#668dff','#3f70ff','#3059d9','#2242b3','#13298d','#041066'],
},
})
```
Y lo pasa al provider:
```tsx
<FnMantineProvider theme={theme} defaultColorScheme="dark">
<App />
</FnMantineProvider>
```
**No** definas variables CSS custom, **no** toques `:root`, **no** cargues hojas de estilo adicionales. Mantine genera todas las variables (`--mantine-color-*`, `--mantine-spacing-*`, `--mantine-radius-*`) automáticamente del `theme`.
---
## 9. Anti-patrones (rechazar en code review)
```tsx
// ❌ className con clases custom
<Button className="bg-blue-500 rounded-lg px-4 py-2">Click</Button>
// ❌ CSS modules, Tailwind, style inline
<div className={styles.card} />
<div className="flex flex-col gap-4" />
<div style={{ display: 'flex', gap: 16 }} />
// ✅ en su lugar
<Stack gap="md" />
// ❌ Mantine directo en apps
import { Button } from '@mantine/core'
// ✅
import { Button } from '@fn_library'
// ❌ Iconos ajenos
import { Plus } from 'lucide-react'
// ✅
import { IconPlus } from '@tabler/icons-react'
// ❌ HTML nativo para formulario
<label>Email<input type="email" /></label>
// ✅
<FormField label="Email"><Input type="email" /></FormField>
// ❌ <table> a mano
<table><thead>...</thead><tbody>...</tbody></table>
// ✅
<DataTable data={rows} columns={cols} />
// ❌ Recharts directo
import { LineChart } from 'recharts'
// ✅
import { LineChart } from '@fn_library'
```
---
## 10. Checklist para handoffs de Claude Design
Antes de integrar un export al repo, verificar:
- [ ] Todos los imports UI vienen de `@fn_library` (excepto layout Mantine: `Stack`, `Group`, `SimpleGrid`, `Grid`, `Paper`, `Box`, `Container`, `Flex`, `Title`, `Text`, `AppShell`).
- [ ] Cero imports de `lucide-react`, `shadcn`, `@radix-ui/*`, `tailwindcss`, `clsx`, `cn`, `cva`, `framer-motion`, `chakra`, `mui`, `antd`.
- [ ] Cero `className` con clases CSS propias (solo se toleran las que el wrapper pasa al nodo raíz para forwardref).
- [ ] Iconos únicamente `@tabler/icons-react`.
- [ ] Charts únicamente `LineChart`/`BarChart`/`AreaChart`/`PieChart`/`Sparkline` del registry.
- [ ] Formulario con `FormField` wrapper para cada campo.
- [ ] Tablas con `DataTable`.
- [ ] KPIs con `KPICard`.
- [ ] Página entera envuelta en `FnMantineProvider` (solo el root).
- [ ] Si aparece un componente que no existe en `@fn_library`, decidir: (a) reescribirlo con wrappers existentes, o (b) extraerlo como función nueva al registry (`frontend/functions/ui/{name}.tsx` + `.md` + `fn index`).
---
## 11. Metadata para Claude Design
Cuando enlaces este repo en `claude.ai/design`, apunta al subdirectorio **`frontend/`** (no a la raíz del monorepo). Lo único que necesita leer para entender el sistema:
```
frontend/
DESIGN_SYSTEM.md ← este documento (regla suprema)
package.json ← dependencias runtime
functions/ui/ ← los 75 componentes con .tsx + .md
index.ts ← barrel @fn_library
```
Si aun así es demasiado, usa el **repo espejo** `fn-design-system` (generado desde este repo) que contiene exactamente esos archivos y nada más.
+23
View File
@@ -0,0 +1,23 @@
# Prompts para Claude Design
Plantillas listas para pegar en `claude.ai/design`. Copias, rellenas los `{PLACEHOLDERS}`, pegas. Cada una asume que el repo `fn-design-system` ya está enlazado al proyecto de Claude Design y que `DESIGN_SYSTEM.md` es la regla suprema.
| # | Archivo | Cuándo usar |
|---|---------|-------------|
| 00 | [onboarding.md](onboarding.md) | **Primera vez** al crear un proyecto nuevo en Claude Design. Le fija las reglas. |
| 01 | [dashboard.md](dashboard.md) | Dashboard de analytics con KPIs + charts. |
| 02 | [crud.md](crud.md) | Gestión CRUD: lista + tabla + modal create/edit/delete. |
| 03 | [detail.md](detail.md) | Ficha de detalle de una entidad. |
| 04 | [settings.md](settings.md) | Página de configuración con navegación lateral. |
| 05 | [auth.md](auth.md) | Login / register. |
| 06 | [error.md](error.md) | 404 / 500 / 403 / custom. |
| 07 | [custom.md](custom.md) | Pantalla libre que no encaja en ninguna plantilla. |
| 08 | [handoff_integrate.md](handoff_integrate.md) | **Para Claude Code (yo)** — integrar un export de Claude Design al registry. |
| 09 | [questionnaire.md](questionnaire.md) | **Guía del agente** — cuestionario numerado para rellenar plantillas rápido. |
## Flujo estándar
1. **Primera vez en el proyecto** → pegas `onboarding.md`.
2. **Cada pantalla nueva** → dices al agente "quiero un prompt de dashboard / crud / ..." y el agente te lanza el **cuestionario numerado** (ver `questionnaire.md`). Respondes con `1,2,3` y él construye el prompt completo.
3. **Cuando te guste el diseño** → "Handoff to Claude Code" en Claude Design.
4. **De vuelta aquí** → me pegas el export + `handoff_integrate.md`, yo lo adapto al repo.
+37
View File
@@ -0,0 +1,37 @@
# Login / Register
Diseña una página de autenticación usando `AuthForm` del registry. Respeta `DESIGN_SYSTEM.md`.
## Config
- **Modo por defecto:** `login` / `register` / `toggle` (con switch entre ambos)
- **Nombre de la app:** `{NOMBRE}` (aparece en el header)
- **Logo:** {icono Tabler / SVG inline / texto}
## Campos
### Login
- `email` · text · required
- `password` · password_input · required + strength meter: {sí/no}
- `remember_me` · switch_toggle · default true
### Register (extra a los de login)
- `full_name` · text · required
- `organization` · text · optional
- `terms` · checkbox · required · label: "Acepto los términos y condiciones"
## Social buttons (opcional)
{marca los que aplican}
- [ ] Google (IconBrandGoogle)
- [ ] GitHub (IconBrandGithub)
- [ ] GitLab (IconBrandGitlab)
- [ ] SSO corporativo (IconShieldLock, texto "Continue with SSO")
## Footer
- Link "¿Olvidaste la contraseña?" → `/forgot-password`
- Link "¿No tienes cuenta? Regístrate" (solo en modo login)
## Layout
- Centrado vertical y horizontal en viewport.
- Card con `variant="default"`, radius `lg`, padding `xl`, max-width `400px`.
- Fondo: color scheme dark (por defecto) con un sutil patrón o gradiente: {describe o "sin fondo"}.
Entrégalo listo para Handoff to Claude Code.
+43
View File
@@ -0,0 +1,43 @@
# CRUD
Diseña una página de gestión de `{ENTIDAD}` usando `crudPage<T>` como generator raíz. Respeta `DESIGN_SYSTEM.md`.
## Entidad
TypeScript:
```ts
interface {Entidad} {
{campo1}: {tipo}
{campo2}: {tipo}
// ...
}
```
## Columnas de la tabla
| key | label | format / render |
|-----|-------|-----------------|
| {id} | ID | texto corto |
| {nombre} | Nombre | texto |
| {status} | Status | Badge semántico (success/destructive/warning según valor) |
| {price} | Precio | format=currency |
| {created} | Creado | format=datetime |
## Acciones
- Header: `{Add Entidad}` (button default + IconPlus)
- Por fila (en DropdownMenu): {Edit, Delete, Ver detalle, Duplicar}
## Form schema (modal create/edit)
| field | type | label | validación |
|-------|------|-------|------------|
| {nombre} | text | Nombre | required |
| {categoria} | select | Categoría | required, opciones: [...] |
| {price} | number | Precio | min 0 |
| {notes} | textarea | Notas | — |
| {active} | toggle | Activo | default true |
## Filtros
Barra de filtros arriba: {lista de SearchBar / SimpleSelect / Chips / DatePickerInput}
## Notas
- {paginación / sort / bulk actions si aplican}
Entrégalo listo para Handoff to Claude Code. Mock data realista.
+44
View File
@@ -0,0 +1,44 @@
# Pantalla custom (sin page generator)
Cuando la pantalla no encaja en ninguno de los generators (`analyticsPage`, `crudPage`, `detailPage`, `settingsPage`, `AuthForm`, `ErrorPage`), usa esta plantilla. Respeta `DESIGN_SYSTEM.md`.
## Objetivo
{Describe en 1-2 frases qué hace la pantalla y qué problema resuelve para el usuario.}
## Shell
- ¿Va dentro de `FnAppShell` (header + navbar)? → `{sí/no}`. Si sí:
- Header: {título app, búsqueda global, avatar menu, notificaciones}
- Navbar: {items principales con FnNavLink + iconos Tabler}
- Sticky `PageHeader` con tabs: `{sí/no}`
## Layout principal
Describe la composición en términos de primitives de Mantine (`Stack`, `Group`, `SimpleGrid`, `Grid`, `Paper`, `Box`) y wrappers de `@fn_library`.
Ejemplo:
```
Stack gap="lg"
├── PageHeader (título + acciones)
├── SimpleGrid cols={2}
│ ├── Card (KpiCard list)
│ └── Card (LineChart)
└── DataTable
```
## Componentes requeridos (todos desde @fn_library)
- {lista exhaustiva de componentes que debes usar — KpiCard, DataTable, Command, Dialog, etc.}
## Interacciones
- {clics, hovers, modales que se abren, estados de loading, empty states, errores}
## Estados
- **Loading:** {Skeleton variant / LoadingOverlay}
- **Empty:** EmptyState con {icono, mensaje, acción}
- **Error:** {Alert destructive / toast}
## Datos (mock)
{Pega o describe el shape de los datos; JSON realista de 3-5 filas si es tabla, arrays de puntos si es chart, etc.}
## Restricciones
- {lo que NO debe hacer — ej. "sin modal global, todo inline"}
Entrégalo listo para Handoff to Claude Code.
+36
View File
@@ -0,0 +1,36 @@
# Dashboard de analytics
Diseña una página de analytics usando `analyticsPage` como generator raíz. Respeta `DESIGN_SYSTEM.md`.
## Contexto
- **Dominio:** {QUÉ MIDE — ej. monitoreo de pipelines / ventas / tráfico web}
- **Audiencia:** {QUIÉN LO VE — ej. operador, ejecutivo}
- **Frecuencia:** {tiempo real / cada hora / diario}
## Header
- Título: `{TÍTULO}`
- Subtítulo: `{SUBTÍTULO — ej. "últimas 24h"}`
- Acciones (botones a la derecha): {LISTA — ej. "Refresh" (outline + IconRefresh), "Export" (ghost + IconDownload)}
- Date range selector: {sí / no}
## KPIs (fila de KpiCards)
Entre 2 y 6. Para cada uno:
| label | value | unit | delta |
|-------|-------|------|-------|
| {Runs totales} | {1284} | — | +12% positivo |
| {Success rate} | {97.3} | % | +0.4% positivo |
| {P95 latency} | {842} | ms | -8% positivo (bajar es bueno) |
| {Errores} | {34} | — | +5 negativo destructivo |
## Charts
{ENTRE 2 Y 6 charts. Para cada uno:}
- **id** · título · tipo (`line` / `bar` / `area` / `pie`) · span (`1` o `2`) · series
- Ej. `runs` · "Runs por hora" · `line` · span 1 · series `success` + `failure`
- Ej. `top` · "Top pipelines" · `bar` · span 1 · series `count`
- Ej. `latency` · "Latencia por etapa" · `area` · span 2 · stacked · series `p50` + `p95`
- Ej. `domain` · "Distribución por dominio" · `pie` · span 1 · donut
## Notas
- {Restricciones adicionales, ej. "sin header, se embebe en otra página"}
Entrégalo listo para Handoff to Claude Code. Los datos pueden ser mock realistas.
+36
View File
@@ -0,0 +1,36 @@
# Ficha de detalle
Diseña la página de detalle de `{ENTIDAD}` usando `detailPage` como generator raíz. Respeta `DESIGN_SYSTEM.md`.
## Header
- Avatar: {iniciales / imagen / icono Tabler}
- Título: `{nombre de la entidad}`
- Subtítulo: `{descripción breve}`
- Badge lateral: `{dominio / status / tipo}` — variante `{secondary / success / destructive}`
- Back button → `{ruta}`
- Acciones (derecha): {Edit (outline), Delete (destructive), Duplicate (ghost)}
## Campos (grid 2-3 columnas)
| label | value |
|-------|-------|
| {ID} | {valor} |
| {Creado} | {fecha formateada} |
| {Autor} | {texto + avatar pequeño} |
| {Estado} | {Badge} |
| {Tags} | {Chips} |
## Tabs
| key | label | count | contenido |
|-----|-------|-------|-----------|
| {code} | Código | {N} | `<pre>` con código |
| {tests} | Tests | {M} | DataTable de tests |
| {deps} | Dependencias | {K} | Lista con badges |
| {usage} | Uso | {J} | DataTable |
## Timeline de actividad
Lista de eventos: `{created, updated, indexed, deployed, ...}` con fecha relativa.
## Notas
- {si hay secciones custom, descríbelas aquí}
Entrégalo listo para Handoff to Claude Code.
+25
View File
@@ -0,0 +1,25 @@
# Página de error
Diseña una página de error usando `ErrorPage` del registry. Respeta `DESIGN_SYSTEM.md`.
## Config
- **Código:** `{404 / 500 / 403 / 503 / CUSTOM}`
- **Título:** `{ej. "Página no encontrada"}`
- **Descripción:** `{una o dos frases — sin terminología técnica salvo que sea para devs}`
- **Icono (opcional):** `{IconBug / IconServerOff / IconLock / ...}` de Tabler
## Acciones
{una o dos botones, la primera es la primaria}
- `{Volver al inicio}``/` (default)
- `{Contactar soporte}``mailto:...` (outline)
- `{Reintentar}``() => window.location.reload()` (ghost)
## Layout
- Centrado vertical y horizontal.
- Código del error: display grande (fz 80-120, fw 700, color dimmed o destructive según gravedad).
- Debajo: título, descripción, acciones en `Group`.
## Notas
- {si hay contexto adicional — ej. incluir request-id para debugging}
Entrégalo listo para Handoff to Claude Code.
@@ -0,0 +1,29 @@
# Handoff Integrate — prompt para Claude Code (yo)
Usa este prompt cuando tengas el export de Claude Design y quieras que lo integre al registry.
---
Acabo de terminar un diseño en Claude Design y te paso el export. Quiero que lo integres en `fn_registry` siguiendo `frontend/DESIGN_SYSTEM.md`.
## Destino
- **App:** `apps/{APP}/` (si no existe, dímelo y decidimos si se crea).
- **Ruta / componente:** `{src/pages/Whatever.tsx}` o similar.
## Contexto del diseño
- **Tipo:** `{dashboard / crud / detail / settings / auth / error / custom}`
- **Propósito:** `{1-2 frases}`
## Export de Claude Design
{pega aquí los ficheros `.tsx` que te dio el handoff. Si son varios, uno bajo cada encabezado `### path/to/file.tsx` con el código dentro de ``` ```.}
## Qué necesito que hagas
1. **Verifica** que todo lo importable de UI viene de `@fn_library` (no `@mantine/core` directo, no HTML nativo, no librerías ajenas). Si el export usa otra cosa, reescríbelo con wrappers del registry.
2. **Valida** la deny-list: cero `tailwindcss`, `clsx`, `cn`, `cva`, `lucide-react`, `shadcn`, `@radix-ui/*`, `framer-motion`, `chakra`, `mui`, `antd`, CSS modules, className con clases propias, style inline para maquetar.
3. **Migra** cualquier icono a `@tabler/icons-react`.
4. **Coloca** los ficheros bajo `apps/{APP}/frontend/src/` respetando la estructura existente.
5. Si aparece un **componente que no existe en `@fn_library` pero tiene sentido como primitiva reutilizable**, dime: propones extraerlo a `frontend/functions/ui/{nombre}.tsx` + `.md` y corremos `fn index`. No lo hagas sin confirmar.
6. **Verifica** con `pnpm tsc --noEmit` o `pnpm build` que compila. Si no, arregla los imports.
7. **Reporta** en tres líneas: qué integraste, qué cambiaste del export, y qué quedó pendiente.
No inventes features. No añadas lógica que no estaba en el export. Integración fiel + adaptación al stack.
+23
View File
@@ -0,0 +1,23 @@
# Onboarding — pegar al crear el proyecto en Claude Design
Voy a usarte para diseñar UI de un sistema con design system propio llamado `@fn_library`.
**Regla suprema:** lee `DESIGN_SYSTEM.md` en la raíz del repo enlazado. Es la fuente de verdad. No improvises componentes ni estilos fuera de lo que ese documento define.
**Resumen de reglas duras:**
- Stack: React 19 + Mantine v9 + `@tabler/icons-react` + `@mantine/charts`.
- Solo importas UI desde `@fn_library` (barrel). Nunca `@mantine/core` directo en las apps.
- Layout (`Stack`, `Group`, `SimpleGrid`, `Grid`, `Paper`, `Box`, `Container`, `AppShell`, `Title`, `Text`): **sí** desde `@mantine/core`.
- Iconos: solo `@tabler/icons-react` (nunca `lucide-react`).
- Prohibido: Tailwind, `cn()`, CVA, `clsx`, `className` con clases propias, `style` inline para maquetar, Recharts directo, shadcn, Radix directo, Chakra, MUI.
- Props Mantine (`size`, `color`, `variant`, `p`, `m`, `gap`, `radius`, `shadow`, `c`, `fw`, `fz`) en vez de CSS.
- Page generators preferidos antes de montar layouts a mano: `analyticsPage`, `crudPage`, `detailPage`, `settingsPage`, `dashboardLayout`, `AuthForm`, `ErrorPage`.
- Root de cada app envuelto en `FnMantineProvider` con `defaultColorScheme="dark"`.
- Color scheme por defecto: **dark**.
**Antes de generar nada, confírmame que has leído `DESIGN_SYSTEM.md`** listándome:
1. Los 7 page generators y cuándo usa cada uno.
2. Cinco primitives que usarías para un dashboard de analytics.
3. Los tres imports que aparecen en el root de toda app (provider + tema + estilos).
Cuando te pida una pantalla, entrégamela lista para **Handoff to Claude Code**.
+349
View File
@@ -0,0 +1,349 @@
# Questionnaire — arranque rápido para prompts de Claude Design
Cuando el usuario pida un prompt (ej. "quiero un dashboard", "prompt para crud de clientes", "hazme una settings"), el agente **NO** le pregunta todo en abierto. Le hace el cuestionario numerado de abajo, una tanda de preguntas cada vez, con opciones `1) 2) 3) 4) 5)` para que responda rápido con los números correspondientes (`1,3,2,4` o similar).
## Reglas del flujo
1. **Detecta el template** que aplica (dashboard / crud / detail / settings / auth / error / custom).
2. **Pregunta en tandas de 4-6** preguntas numeradas. No todo de golpe.
3. Si una pregunta es **texto libre**, márcala como `(abierta)`; el usuario contesta con texto en vez de número.
4. Si una respuesta necesita opción fuera del menú, el usuario escribe `"otra: <texto>"` y continúa.
5. Al final de **todas** las tandas, recapitulas las respuestas en una tabla y generas el prompt de Claude Design rellenado, listo para copiar.
6. Si el usuario responde con un número fuera de rango o ambiguo, repite la pregunta con el rango válido resaltado.
### Formato de respuesta del usuario
Puede contestar:
- `1,2,3,1,2` — una respuesta por pregunta en el orden.
- `1) 2 2) 3 3) 1` — explícito por número de pregunta.
- Mezcla libre con números y texto: `1, "dashboard ventas", 3, 4, 2`.
---
## Template: dashboard
### Tanda 1 — Contexto
**Q1.** Tipo de dashboard:
1) Monitoreo técnico (latencias, errores, uptime)
2) Business analytics (ventas, conversiones, ingresos)
3) Contenido / tráfico (usuarios, pageviews, engagement)
4) Operacional (inventario, órdenes, tareas)
5) Otro *(abierta — describe)*
**Q2.** Título del dashboard *(abierta)*.
**Q3.** Subtítulo / contexto temporal:
1) "Tiempo real"
2) "Últimas 24h"
3) "Últimos 7 días"
4) "Último mes"
5) Configurable por usuario (date range picker)
**Q4.** Audiencia principal:
1) Operador técnico
2) Ejecutivo / C-level
3) Cliente externo
4) Equipo interno mixto
### Tanda 2 — KPIs
**Q5.** Número de KPIs (fila superior):
1) 2 2) 3 3) 4 4) 5 5) 6
**Q6.** Estilo de deltas:
1) Porcentaje con flecha (▲ +12%)
2) Valor absoluto (+123)
3) Mixto (algunos % otros absolutos)
4) Sin deltas
**Q7.** ¿Incluyen sparkline inline el KPI?
1) Sí, todos los KPIs
2) Solo los KPIs principales (1-2)
3) No
### Tanda 3 — Charts
**Q8.** Número de charts:
1) 2 2) 3 3) 4 4) 5 5) 6
**Q9.** Mix de tipos de chart:
1) Solo line (time-series)
2) line + bar (evolución + comparación)
3) line + bar + pie (variado)
4) area stacked + line (distribución + serie)
5) Otro *(abierta — describe la combinación)*
**Q10.** ¿Hay un chart principal (span 2, full width)?
1) Sí, el primero
2) Sí, el último
3) Varios con span 2
4) Todos span 1 (grid uniforme)
### Tanda 4 — Header y controles
**Q11.** Acciones en el header:
1) Solo Refresh
2) Refresh + Export
3) Refresh + Date range picker
4) Refresh + Export + Date range picker
5) Ninguna
**Q12.** Color scheme:
1) Dark (por defecto)
2) Light
3) Sigue la preferencia del sistema
**Q13.** ¿Va dentro de `FnAppShell` (con header + navbar)?
1) Sí — especifica qué items lleva el navbar *(abierta si eliges esto)*
2) No — la página ocupa pantalla completa sin shell
---
## Template: crud
### Tanda 1 — Entidad
**Q1.** Nombre de la entidad *(abierta, PascalCase. Ej. "Customer", "DeployTarget")*.
**Q2.** ¿Cuántas columnas visibles en la tabla?
1) 3 2) 4 3) 5 4) 6 5) 7+
**Q3.** Lista las columnas *(abierta, separadas por coma)* — para cada una indica si es texto / número / fecha / badge / acción.
### Tanda 2 — Acciones
**Q4.** Acciones por fila:
1) Solo Edit
2) Edit + Delete
3) Edit + Delete + Duplicate
4) Edit + Delete + "Ver detalle"
5) Custom *(abierta)*
**Q5.** Acciones del header:
1) Solo "Add"
2) "Add" + "Import"
3) "Add" + "Export"
4) "Add" + "Import" + "Export"
5) Bulk actions (select rows + apply)
**Q6.** ¿Modal o página separada para create/edit?
1) Modal (Dialog)
2) Drawer lateral (Sheet)
3) Página completa aparte
### Tanda 3 — Form
**Q7.** Número de campos en el form *(1-5, 6-10, 11+)*:
1) 1-5 2) 6-10 3) 11+
**Q8.** Lista los campos *(abierta — nombre + tipo cada uno)*.
**Q9.** ¿Validación?
1) Básica (required only)
2) Completa (email, rangos, regex)
3) Async (verificar contra API)
4) Sin validación
### Tanda 4 — Lista
**Q10.** Filtros sobre la tabla:
1) Solo SearchBar global
2) SearchBar + SimpleSelect (por categoría)
3) SearchBar + Chips (tags activos)
4) Filtros complejos (multi-select + date range)
5) Sin filtros
**Q11.** Paginación:
1) Pagination componente (numbers)
2) Infinite scroll
3) "Load more" button
4) Sin paginación (máximo 100 filas)
**Q12.** ¿Sorting en columnas?
1) Sí, en todas
2) Solo en 2-3 principales
3) No
---
## Template: detail
### Tanda 1 — Entidad
**Q1.** Nombre de la entidad *(abierta)*.
**Q2.** Avatar del header:
1) Iniciales generadas
2) Imagen (URL)
3) Icono Tabler *(abierta — cuál)*
4) Sin avatar
**Q3.** Badge lateral:
1) Status (success/warning/destructive)
2) Tipo / categoría (secondary)
3) Ambos
4) Sin badge
### Tanda 2 — Contenido
**Q4.** Número de campos en el grid:
1) 3 2) 4-6 3) 7-10 4) 11+
**Q5.** Lista los campos *(abierta — label + tipo)*.
**Q6.** Número de tabs:
1) 1 (sin tabs) 2) 2 3) 3 4) 4 5) 5+
**Q7.** Tabs *(abierta — lista de keys + labels + tipo de contenido de cada una)*.
### Tanda 3 — Extras
**Q8.** ¿Timeline de actividad?
1) Sí, vertical bajo los tabs
2) Sí, en un tab propio
3) Sí, en sidebar derecho
4) No
**Q9.** Acciones del header:
1) Solo Edit
2) Edit + Delete
3) Edit + Delete + Duplicate
4) Edit + Delete + Share
5) Custom *(abierta)*
---
## Template: settings
### Tanda 1 — Estructura
**Q1.** Número de secciones (items del nav lateral):
1) 2-3 2) 4-5 3) 6-8 4) 9+
**Q2.** Lista las secciones *(abierta — key + label + icono Tabler sugerido para cada una)*.
**Q3.** Modo de guardado:
1) Botones "Cancelar" + "Guardar" al pie de cada sección
2) Autosave con toast de confirmación
3) Un solo "Guardar" global
### Tanda 2 — Campos
**Q4.** Tipos de campos más comunes:
1) Solo texto y toggles
2) Texto + toggle + select
3) Todo lo anterior + file upload (avatar, etc.)
4) Todo + campos complejos (2FA, sessions, API keys)
**Q5.** ¿Hay sección de seguridad? (password, 2FA, sessions, api keys)
1) Sí 2) No
**Q6.** ¿Hay sección de facturación? (plan, tarjetas, historial)
1) Sí 2) No
---
## Template: auth
### Tanda 1 — Modo
**Q1.** Modo:
1) Solo login
2) Solo register
3) Toggle login/register
**Q2.** Nombre de la app *(abierta)* (aparece en el header).
**Q3.** Logo / marca:
1) Icono Tabler *(abierta — cuál)*
2) SVG inline
3) Texto (nombre de la app)
4) Sin logo
### Tanda 2 — Campos
**Q4.** Campos en register extra a email/password:
1) Solo full_name
2) full_name + organization
3) full_name + organization + role
4) Ninguno (solo email + password)
5) Otro *(abierta)*
**Q5.** Password strength meter:
1) Sí 2) No
**Q6.** Remember me:
1) Sí 2) No
### Tanda 3 — Social
**Q7.** Proveedores OAuth *(puedes elegir varios — ej. 1,2)*:
1) Google
2) GitHub
3) GitLab
4) Microsoft
5) SSO corporativo
6) Ninguno (solo email/password)
**Q8.** Terms & conditions checkbox en register:
1) Sí, required 2) No
---
## Template: error
**Q1.** Código:
1) 404 2) 500 3) 403 4) 503 5) Custom *(abierta)*
**Q2.** Tono del mensaje:
1) Formal y breve
2) Amigable / humorístico
3) Técnico (para devs)
**Q3.** Icono Tabler:
1) IconBug (error genérico)
2) IconServerOff (backend down)
3) IconLock (permission denied)
4) IconSearchOff (not found)
5) Otro *(abierta)*
**Q4.** Acciones:
1) Solo "Volver al inicio"
2) "Volver al inicio" + "Contactar soporte"
3) "Volver al inicio" + "Reintentar"
4) Las tres
**Q5.** ¿Mostrar request-id / trace-id para debugging?
1) Sí 2) No
---
## Template: custom
**Q1.** Descripción del objetivo de la pantalla *(abierta — 1-2 frases)*.
**Q2.** ¿Dentro de `FnAppShell`?
1) Sí, con header + navbar
2) Sí, solo header
3) No, pantalla completa
**Q3.** Primitivas principales (puedes marcar varias con coma):
1) Card + KpiCard
2) DataTable
3) Charts (line/bar/area/pie)
4) Command (combobox cmdk)
5) GraphContainer (sigma.js)
6) Timeline
7) Dropzone (upload)
8) Stepper
9) Otro *(abierta)*
**Q4.** Estados a cubrir:
1) Loading + Empty + Error
2) Loading + Empty
3) Solo loading
4) Ninguno (datos siempre presentes)
**Q5.** Interactividad:
1) Solo lectura
2) Formulario / edición
3) Drag & drop
4) Realtime (SSE / WebSocket)
5) Mix *(abierta)*
**Q6.** Data mock *(abierta — pega JSON o describe)*.
---
## Salida final del agente
Cuando el usuario haya respondido todas las tandas, el agente:
1. **Recapitula** las respuestas en una tabla Markdown `| # | Pregunta | Respuesta |`.
2. **Genera el prompt completo** de Claude Design con todos los placeholders rellenos, tomando el template correspondiente de `frontend/design_prompts/{template}.md`.
3. **Entrega el prompt final** en un bloque de código copiable.
4. Pregunta al final: *"¿Copio al portapapeles / abres Claude Design?"* o *"¿Ajustamos algo antes?"*.
+42
View File
@@ -0,0 +1,42 @@
# Página de configuración
Diseña una página de settings usando `settingsPage` como generator raíz. Respeta `DESIGN_SYSTEM.md`.
## Navegación lateral
| key | label | icono Tabler |
|-----|-------|--------------|
| {general} | General | IconSettings |
| {profile} | Perfil | IconUser |
| {notifications} | Notificaciones | IconBell |
| {security} | Seguridad | IconLock |
| {billing} | Facturación | IconCreditCard |
| {api_keys} | API Keys | IconKey |
## Secciones (una por key de nav)
### {general}
- `{site_name}` · text · "Nombre del sitio"
- `{timezone}` · select · opciones `[UTC, Europe/Madrid, ...]`
- `{language}` · simple_select · `[es, en, fr]`
### {profile}
- `{full_name}` · text · required
- `{email}` · text · required, validación email
- `{avatar}` · file_input · image/*
- `{bio}` · textarea · maxlength 500
### {notifications}
- `{email_alerts}` · toggle · default true
- `{daily_digest}` · toggle · default false
- `{quiet_hours}` · date_picker_input (range)
### {security}
- Password change (PasswordInput old + new + confirm)
- 2FA toggle
- Active sessions list (DataTable + "Revoke" per row)
## Guardado
- Botones al pie de cada sección: "Cancelar" (ghost) + "Guardar cambios" (default).
- O autosave con toast de confirmación (si lo prefieres, explícalo aquí).
Entrégalo listo para Handoff to Claude Code.