ESP32-S3 Bench Guide
ESP32-S3 N16R8 bench guide: live pinout, lessons, simulators and the bench log.
翻译准备中
尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证Plataforma web 3D interactiva para simulación de escenarios psicológicos con pacientes virtuales controlados por IA. Trabajo de Grado - Ingeniería de Sistemas, Universidad del Valle.
翻译准备中
取自仓库 README
正在加载完整 README。
查看完整 README ↗Plataforma web 3D para la simulación de escenarios de práctica clínica orientada a estudiantes de psicología, con pacientes virtuales (NPC) conducidos por inteligencia artificial.
Trabajo de grado — Brayan Steven Narváez Valdés, Universidad del Valle.
| Entorno | URL | Rama |
|---|---|---|
| Producción | https://psysim.vercel.app | main |
| Staging (preview) | https://psysim-git-develop-brayan-steven-narvaez-valdezs-projects.vercel.app | develop |
| Previews de otras ramas | https://psysim-git-<rama>-brayan-steven-narvaez-valdezs-projects.vercel.app | cualquier |
Los despliegues de preview (incluido staging) están protegidos por Vercel Authentication: solo los miembros del equipo de Vercel pueden abrirlos. Ver Tests E2E contra staging.
| Capa | Tecnología |
|---|---|
| Framework | Next.js 16 (App Router, Turbopack, proxy.ts) · React 19 · TypeScript |
| UI | Tailwind CSS 4 · shadcn/ui (Radix) · lucide-react |
| Estado | Zustand 5 (store por árbol de React, slices) |
| Validación | Zod 4 · React Hook Form |
| Backend / BaaS | Supabase: Auth, PostgreSQL con RLS |
| IA | Google Gemini vía Vercel AI SDK (ai + @ai-sdk/google) |
| Calidad | ESLint 9 · Prettier · Husky + lint-staged + commitlint |
| Pruebas | Vitest + Testing Library (unitarias) · Playwright (E2E) |
| Despliegue | Vercel (Node.js 24) |
Requisitos: Node.js 24 (ver .nvmrc) y pnpm 11 (corepack enable).
git clone https://github.com/stevenD18skz/psysim.git
cd psysim
pnpm install # instala dependencias y activa los git hooks
cp .env.example .env.local # o: vercel link && vercel env pull .env.local
pnpm dev # http://localhost:3000
Plantilla completa en .env.example. .env.local nunca se versiona.
| Variable | Dónde | Descripción |
|---|---|---|
NEXT_PUBLIC_SUPABASE_URL | cliente/servidor | URL del proyecto de Supabase |
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY | cliente/servidor | Clave publicable sb_publishable_… (antes ANON_KEY) |
SUPABASE_SECRET_KEY | solo servidor | Clave secreta sb_secret_… (antes SERVICE_ROLE_KEY). Omite RLS |
AI_API_KEY | solo servidor | API key de Google Gemini |
AI_MODEL | solo servidor | Modelo de Gemini (por defecto gemini-3.5-flash) |
Las variables se validan con Zod al arrancar (src/lib/env): si falta alguna, el error indica cuál.
El esquema se versiona como migraciones SQL en supabase/migrations.
export SUPABASE_ACCESS_TOKEN=sbp_... # token personal de Supabase
pnpm exec supabase link --project-ref afqofsneoblsjjdvktsu
pnpm exec supabase db push # aplica migraciones pendientes
pnpm db:types # regenera src/types/database.types.ts
pnpm db:seed # crea las cuentas de prueba (idempotente)
pnpm db:seed crea tres docentes de prueba (docente{1,2,3}@psysim.test) y una cuenta sin rol
([email protected]) para probar el acceso denegado. Las contraseñas se generan aleatoriamente y
se muestran una sola vez (pnpm db:seed --reset las regenera).
Configuración de Auth aplicada al proyecto (también reflejada en supabase/config.toml):
public.custom_access_token_hook activado (añade el rol al JWT).| Parámetro | Valor |
|---|---|
| Proveedor | Google Gemini (Generative Language API) vía Vercel AI SDK 7 |
| URL base | https://generativelanguage.googleapis.com/v1beta |
| Modelo principal | gemini-3.5-flash (AI_MODEL), razonamiento minimal |
| Modelo respaldo | gemini-3.5-flash-lite (AI_MODEL_RESPALDO) |
| Límites | 12 s para el principal, 25 s en total, maxDuration de 30 s |
Se eligió gemini-3.5-flash por latencia (≈1 s por respuesta corta frente a ≈3–7 s de
gemini-3.8-flash), clave para un diálogo fluido con el paciente virtual. Como la latencia del
proveedor varía (el 29/09/2026 gemini-3.5-flash llegó a 11–16 s y a devolver 503), el Route
Handler pasa automáticamente al modelo de respaldo si el principal no responde en 12 s o está
saturado. Verificar la conexión:
pnpm ai:test
Route Handler POST /api/npc/chat (HU-12): recibe sesion_id, npc_id, mensaje_usuario e
historial; verifica la sesión del estudiante (401/403), valida con Zod (400), carga la sesión con
RLS (404 si no es del estudiante, 409 si ya finalizó o no ha comenzado), reserva un turno del
límite de 10 mensajes por minuto (429 con Retry-After; función reservar_peticion_ia), llama a
la IA con el prompt de la sesión (el que configuró el docente al asignar el caso) y guarda el
intercambio en la tabla mensaje. Responde respuesta_npc, timestamp_respuesta,
tokens_entrada y tokens_salida. Los errores de la IA se traducen a 504 (tiempo agotado), 503
(saturación) o 502, sin exponer detalles.
| Script | Descripción |
|---|---|
pnpm dev | Servidor de desarrollo (Turbopack) |
pnpm build / start | Build de producción / servidor de producción |
pnpm lint / lint:fix | ESLint (cero advertencias permitidas) |
pnpm typecheck | Verificación de tipos con tsc |
pnpm format / format:check | Prettier |
pnpm test | Tests unitarios (Vitest) |
pnpm test:coverage | Tests unitarios con cobertura |
pnpm test:e2e | Tests E2E (Playwright). Levanta pnpm dev si hace falta |
pnpm audit:deps | Vulnerabilidades conocidas de las dependencias (pnpm audit) |
pnpm check | lint + typecheck + formato + tests unitarios |
pnpm db:types | Regenera los tipos de la base de datos |
pnpm db:seed | Crea las cuentas semilla |
pnpm modelos:inspeccionar | Informe de los GLB de public/models (medidas, animaciones) |
pnpm modelos:subir | Optimiza los GLB con Draco y los sube a Supabase Storage |
pnpm supabase:test | Verifica conexión, RLS y claims del JWT con Supabase |
pnpm ai:test | Verifica la conexión con la API de IA |
src/
├── app/
│ ├── (auth)/login/ ← Login del docente (formulario + Server Action)
│ ├── (protected)/ ← Rutas que exigen sesión de docente
│ │ ├── layout.tsx ← Verificación autoritativa (DAL) + carga del perfil en Zustand
│ │ ├── configuracion/ ← Escenario o caso propio, paciente virtual y datos del estudiante; /casos crea y edita casos
│ │ ├── resultados/ ← Resultados de una práctica terminada: indicadores e historial (HU-21)
│ │ └── simulacion/ ← Simulación 3D de la sesión en curso (HU-08/09/10)
│ ├── api/npc/chat/ ← Route Handler del paciente virtual (HU-12)
│ ├── api/metrics/save/ ← Route Handler que guarda las métricas y cierra la sesión (HU-19)
│ ├── acceso-denegado/ ← Página para cuentas sin rol docente
│ ├── dev/ ← Herramientas solo de desarrollo (galería de modelos, escenas)
│ └── page.tsx ← Página pública de presentación
├── components/
│ ├── 3d/ ← React Three Fiber: SceneCanvas, SceneLoader, sala, muebles, NPC
│ ├── casos/ ← Constructor guiado de casos, vista previa del prompt y prueba del paciente
│ ├── configuracion/ ← Configurador de la sesión y "Mis casos"
│ ├── simulacion/ ← Simulación: HUD, panel de conversación, errores y cierre
│ ├── layout/ ← Navegación principal, botón de logout
│ └── ui/ ← Componentes de shadcn/ui
├── lib/
│ ├── auth/ ← Rutas, DAL (verificación de sesión/rol), Server Actions
│ ├── conversacion/ ← Máquina de estados del NPC, envío de mensajes y guardado de métricas (HU-13/14/16/20)
│ ├── metrics.ts ← Indicadores agregados de la sesión (HU-18)
│ ├── contraste.ts ← Razón de contraste WCAG para validar el tema (HU-25)
│ ├── escena/ ← Colisiones, encuadre, animaciones, ajuste de modelos, Storage
│ ├── ia/ ← Llamada al modelo de lenguaje con respaldo y clasificación de errores
│ ├── escenarios/ ← Consultas y Server Actions de escenarios y sesiones
│ ├── casos/ ← Casos propios del docente: composición del prompt, opciones y Server Actions
│ ├── env/ ← Validación de variables de entorno
│ └── supabase/ ← Clientes: browser, server, admin (clave secreta), proxy
├── schemas/ ← Esquemas Zod compartidos cliente/servidor
├── store/ ← Store de Zustand (slices)
├── types/ ← Tipos globales y tipos generados de la BD
└── proxy.ts ← Proxy de Next.js (antes middleware)
public/models/ ← Modelos 3D fuente (se publican optimizados en Supabase Storage)
public/scenes/ ← JSON de la escena 3D de cada escenario (docs/modelos-3d.md)
public/draco/ ← Decodificadores Draco autoalojados
supabase/ ← config.toml y migraciones SQL
scripts/ ← Scripts de verificación y seed
e2e/ ← Tests de Playwright
docs/ ← Documentación y registros de pruebas
src/proxy.ts): en cada petición refresca la sesión y verifica la firma del JWT
con getClaims() (claves asimétricas, sin llamada de red). En /configuracion y
/simulacion redirige a /login sin sesión, o a /acceso-denegado si el claim
rol_usuario del JWT no es docente.src/lib/auth/dal.ts): el layout y cada página protegida validan la
sesión con el servidor de Auth (getUser(), detecta sesiones revocadas) y leen el rol de la
tabla usuario. Es la verificación autoritativa.estado y fin.El rol llega al JWT mediante un Custom Access Token Hook de Supabase, lo que evita consultar la base de datos en cada petición del proxy.
La simulación monta la escena descrita por public/scenes/e-XX.json (validado con Zod): una sala
procedural cálida, mobiliario (procedural o GLB desde Supabase Storage, comprimido con Draco),
el paciente virtual y controles en primera persona (WASD + ratón con Pointer Lock) con colisiones
por cajas delimitadoras. El canvas limita el devicePixelRatio a 1,5 y lo baja a 1 si el
rendimiento cae (PerformanceMonitor). Añade ?debug=1 a la URL de la simulación para ver los FPS.
Guía de modelos y escenas: docs/modelos-3d.md.
Al acercarse al paciente (≤ 3,2 m) el estudiante inicia la conversación con la tecla E, un clic sobre él o el botón "Conversar con …". La cámara se desplaza con suavidad hasta quedar frente al paciente y aparece el panel de conversación (Enter envía, Shift + Enter inserta una línea).
El estado del paciente sigue la máquina de estados de la sección 4.3.4
(src/lib/conversacion/estados-npc.ts): inactivo → esperando_input → procesando → respondiendo → esperando_input, con error_comunicacion (Reintentar / Finalizar sesión) y sesion_finalizada.
Las transiciones no válidas se rechazan (p. ej. un doble envío). El paciente reacciona a cada
estado: sigue al estudiante con la mirada, baja la cabeza al reflexionar y asiente y mueve la boca
al responder; con un GLB riggeado usa sus clips Idle, Pensando y Hablando con fundidos de
0,3 s. La conversación se guarda en la tabla mensaje y se recupera al recargar la página.
| Rama | Propósito | Despliegue |
|---|---|---|
main | Código estable en producción | Producción (Vercel) |
develop | Integración continua de funcionalidades | Staging (preview) |
feature/* | Nuevas funcionalidades (desde develop) | Preview |
fix/* | Corrección de errores | Preview |
hotfix/* | Correcciones urgentes sobre main | Preview |
docs/* | Documentación | Preview |
refactor/* | Refactorización sin cambios funcionales | Preview |
Flujo: feature/* → PR a develop (staging) → PR de develop a main (producción).
Se usa Conventional Commits, validado por
commitlint en el hook commit-msg:
feat(auth): implementar login con Supabase
fix(proxy): conservar cookies al redirigir
docs(readme): documentar variables de entorno
Tipos: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert.
lint-staged (ESLint --fix + Prettier sobre los archivos preparados) y
pnpm typecheck. El commit se rechaza si algo falla.commitlint.pnpm test # unitarias (Vitest)
pnpm test:coverage # con cobertura: falla si app/api, lib o store bajan del 70 %
pnpm test:e2e:install # (una vez) instala Chromium para Playwright
pnpm test:e2e # E2E contra http://localhost:3000
pnpm test:e2e e2e/accesibilidad.spec.ts # un solo archivo
Los tests están separados por funcionalidad (un archivo por historia o módulo), no en una suite única:
| Funcionalidad | Unitarias | E2E (Playwright) |
|---|---|---|
| Acceso y rutas protegidas (HU-02–04) | lib/auth/*.test.ts, proxy.test.ts | autenticacion.spec.ts |
| Configuración y casos (HU-06/07) | lib/casos/*.test.ts, components/configuracion/*.test.tsx | configuracion.spec.ts |
| Conversación con el paciente (HU-12) | app/api/npc/chat/route.test.ts, lib/conversacion/*.test.ts | conversacion.spec.ts |
| Estado global (HU-14) | store/*.test.ts | — |
| HUD y cierre de sesión (HU-17, 20) | components/simulacion/hud-sesion.test.tsx, use-finalizar-sesion.test.tsx | flujo-completo.spec.ts |
| Métricas (HU-18, 19) | lib/metrics.test.ts, schemas/metrics.schema.test.ts, app/api/metrics/save/route.test.ts | resultados.spec.ts |
| Resultados (HU-21) | lib/sesiones/resultados.test.ts, components/resultados/*.test.tsx | resultados.spec.ts |
| Navegación (HU-22) | — | navegacion.spec.ts |
| Instrucciones del caso (HU-23) | components/simulacion/instrucciones-caso.test.tsx | flujos.ts (paso común) |
| Escenarios y prompts (HU-24) | lib/escenarios/prompts-escenarios.test.ts, schemas/escena.schema.test.ts | laboratorio.spec.ts |
| Accesibilidad (HU-25) | lib/contraste.test.ts (contraste WCAG AA del tema) | accesibilidad.spec.ts (axe-core, teclado) |
| Rendimiento 3D y web (HU-28) | lib/rendimiento.test.ts (presupuestos de peso) | rendimiento.spec.ts, rendimiento-web.spec.ts |
| Despliegue y seguridad (HU-29/30) | lib/seguridad.test.ts, lib/seguridad/csp.test.ts, , , |
Los tests E2E autenticados usan las credenciales de .env.test.local (ver .env.example:
E2E_DOCENTE_*, E2E_ESTUDIANTE_*, E2E_ADMIN_*, E2E_SIN_ROL_*); si no existen, esos tests
se omiten. Registros de pruebas por sprint en docs/pruebas/.
PLAYWRIGHT_BASE_URL=https://psysim-git-develop-brayan-steven-narvaez-valdezs-projects.vercel.app \
VERCEL_AUTOMATION_BYPASS_SECRET=<secreto> \
pnpm test:e2e e2e/smoke.spec.ts
.env* está ignorado por Git (excepto .env.example).SUPABASE_SECRET_KEY y AI_API_KEY solo se usan en el servidor (import 'server-only').next.config.ts (X-Frame-Options, nosniff,
Referrer-Policy, Permissions-Policy); HSTS lo añade Vercel.no-store para que ninguna CDN las
almacene.src/proxy.ts),
HSTS y Cross-Origin-Opener-Policy; el paciente de IA arma su contexto en el servidor y descarta
respuestas que copian sus instrucciones. Detalle en docs/auditoria-seguridad.md.pg_graphql desactivada: la aplicación solo usa la API REST.pnpm test incluye un checklist de seguridad automatizado (src/lib/seguridad.test.ts): RLS en
todas las tablas, funciones security definer con search_path fijo, ningún permiso al rol
anónimo, secretos solo en el servidor y encabezados de seguridad.lib/ia/fuga-prompt.test.tslib/ia/historial.test.tslib/env/entorno-documentado.test.tssmoke.spec.ts, csp.spec.ts |