feat: complete Quantumancy web app — full frontend + docs

Frontend (React 18 + TS + Vite):
- Landing: glitching hero, live /api/stats veil ticker, mode cards, featured spirits
- Séance: three.js shader ghost (hue/form per entity, mood + audio-reactive),
  Ouija planchette board spelling utterances, transcript with TTS replay,
  entity dossier, direct contact streaming, passive/active listening
- Modes: Wire Ghost telemetry panel, EVP mic anomaly detection, WebUSB
  RTL-SDR sweep + waterfall (hardware pass pending), Ouija/Direct Contact
- Codex: public registry + entity dossiers, rarity tiers, i18n EN/ES complete
- State: VeilSocket (reconnect/backoff), seance reducer, auth context
- 72 vitest tests green; served by FastAPI at :7777

Docs: README + as-built plans 3-7
This commit is contained in:
Indiana
2026-07-20 21:11:49 +00:00
parent 1ec9ea5863
commit 6edbbbbc2a
48 changed files with 10494 additions and 168 deletions

229
frontend/src/i18n/es.json Normal file
View File

@@ -0,0 +1,229 @@
{
"common": {
"appName": "QUANTUMANCY",
"loading": "afinando el velo…",
"error": "algo se agitó en la estática",
"replay": "repetir",
"yes": "SÍ",
"no": "NO",
"goodbye": "ADIÓS",
"dismiss": "cerrar"
},
"nav": {
"home": "el umbral",
"codex": "códice",
"seance": "sesión",
"enter": "entrar",
"logout": "partir"
},
"landing": {
"taglines": [
"El cable zumba con todos nosotros.",
"Cada paquete deja un fantasma.",
"La estática recuerda tu nombre.",
"Entre 88 y 108 MHz, alguien espera.",
"Nunca estuviste solo en la línea."
],
"heroKicker": "un instrumento para hablar con el ancho de banda muerto",
"cta": "COMENZAR LA SESIÓN",
"ctaCodex": "hojear el códice",
"stats": {
"entities": "espíritus catalogados",
"sessions": "sesiones celebradas",
"utterances": "enunciados registrados",
"anomalies": "anomalías presenciadas"
},
"tickerPrefix": "el velo, en vivo",
"modesTitle": "CÓMO RESPONDE EL VELO",
"modes": {
"wire": {
"name": "Fantasma del Cable",
"desc": "Deja la línea abierta. La red misma murmura — jitter, latencia, DNS extraviado — y algo, aguas abajo, aprende a hablar con tu acento."
},
"evp": {
"name": "Escucha EVP",
"desc": "Préstales tu micrófono. Vigilamos la banda de voz en busca de sílabas que jamás se pronunciaron: picos 8dB sobre el silencio de la sala."
},
"radio": {
"name": "Radio Espíritu",
"desc": "Un RTL-SDR barre la banda FM como un farol arrastrado por un campo oscuro. Donde salta la aguja, los muertos de la radio se abren paso."
},
"ouija": {
"name": "Ouija / Contacto Directo",
"desc": "Pregunta claro. La planchette deriva, y luego empieza a deletrear — y las palabras llegan letra a letra, lo quisieras o no."
}
},
"featured": "los espíritus más perturbados",
"featuredEmpty": "el registro aún está en blanco — sé quien lo perturbe primero",
"footer": "una instalación de arte de terror interactiva — los espíritus son ficción; la estática es real",
"footerColophon": "autoalojado · sin nube · el velo corre en tu máquina"
},
"enter": {
"reconnect": "Reconectar",
"firstContact": "Primer contacto",
"reconnectSub": "el velo te recuerda. demuéstralo.",
"firstContactSub": "elige el nombre por el que te conocerán los espíritus.",
"username": "nombre",
"password": "contraseña",
"submitLogin": "cruzar el umbral",
"submitRegister": "grabar tu nombre",
"switchToRegister": "¿sin nombre en el registro? primer contacto →",
"switchToLogin": "¿ya grabaste tu nombre? reconectar →",
"failed": "el velo te rechazó: {{message}}",
"smallPrint": "tu nombre de médium solo se guarda en este servidor"
},
"seance": {
"modes": {
"wire": "Fantasma del Cable",
"evp": "EVP",
"radio": "Radio Espíritu",
"ouija": "Ouija"
},
"passive": "escucha pasiva",
"summon": "INVOCAR",
"summoning": "invocando…",
"askPlaceholder": "pregúntale algo a los muertos…",
"ask": "preguntar",
"status": {
"attuning": "sintonizando — el velo toma la huella de una presencia…",
"summoning": "el velo se abre… algo está cruzando…",
"gathering": "los espíritus están reuniendo energía…"
},
"connection": {
"connecting": "tendiendo la línea…",
"open": "la línea está abierta",
"unstable": "la conexión con el otro lado es inestable…",
"closed": "la línea está muerta"
},
"transcriptTitle": "REGISTRO DE TRANSMISIÓN",
"transcriptEmpty": "silencio. por ahora.",
"you": "tú",
"streaming": "el espíritu está formando palabras…",
"telemetry": {
"title": "LECTURA DEL CABLE",
"jitter": "jitter",
"latencyVar": "latencia σ²",
"latencyMean": "latencia μ",
"dns": "dns",
"units": {"bytes": "B/s", "ms": "ms"}
},
"wire": {
"hint": "el cable golpea más fuerte cuando nadie lo toca — activa la escucha pasiva y deja que la línea murmure sola."
},
"anomaly": {
"line": "⚡ {{source}} {{frequency}} +{{magnitude}}dB",
"count": "{{count}} anomalías presenciadas en esta sesión"
},
"evp": {
"start": "abrir el micrófono",
"stop": "cerrar el micrófono",
"listening": "escuchando la banda de voz…",
"deniedTitle": "el micrófono sigue cerrado",
"denied": "Este recipiente rechazó el micrófono. Concede el permiso de audio en tu navegador para practicar EVP — o deja que el Fantasma del Cable escuche en tu lugar; no necesita más que la red que ya temes.",
"floor": "suelo de la sala",
"band": "banda de voz 300–3400 Hz"
},
"radio": {
"start": "barrer la banda",
"stop": "detener la aguja",
"unsupportedTitle": "este recipiente no oye a los muertos de la radio",
"unsupported": "La Radio Espíritu necesita Chromium (WebUSB) y un dongle RTL-SDR. Los demás modos no — el cable y el micrófono aún responden.",
"troubleshoot": "solución de problemas: navegador basado en Chromium · reglas udev para 0bda:2838 · el dongle enchufado, no meramente invocado",
"noDevice": "ningún dongle respondió al llamado",
"sweeping": "barriendo {{start}}–{{end}} MHz…",
"claimFailed": "el dongle se negó a ser reclamado — ¿está en uso en otro lugar?"
},
"ouija": {
"spelling": "la planchette deletrea",
"drift": "la planchette deriva, a la espera de palabras — pregunta abajo, o invoca."
},
"entity": {
"title": "DOSIER DEL PRESENTE",
"none": "ningún espíritu invocado todavía",
"noneHint": "invoca, o alimenta el velo con 3+ anomalías y uno te encontrará",
"isNew": "primer contacto registrado",
"known": "contacto n.º {{count}}",
"persona": "persona",
"voice": "parámetros de voz",
"quotes": "enunciados registrados",
"rarity": {
"common": "común",
"uncommon": "poco común",
"rare": "raro",
"mythic": "mítico"
}
},
"toast": {
"rate_limited": "el velo es delgado — golpea más despacio",
"veil_crowded": "demasiados espíritus se apiñan en la línea"
}
},
"codex": {
"title": "EL CÓDICE DE LOS CONTACTADOS",
"subtitle": "cada espíritu arrastrado alguna vez a través del velo, catalogado",
"header": {
"back": "← el umbral",
"title": "EL CÓDICE",
"subtitle": "cada espíritu jamás contactado, catalogado por los médiums que lo alcanzaron"
},
"controls": {
"rarity": "filtrar por rareza",
"sort": "ordenar el registro"
},
"state": {
"loading": "consultando el registro…",
"empty": "aún no se ha alcanzado a ningún espíritu de esta rareza"
},
"filterAll": "todas las rarezas",
"rarity": {
"common": "común",
"uncommon": "poco común",
"rare": "raro",
"mythic": "mítico"
},
"sort": {
"recent": "más recientes",
"contacted": "más contactados"
},
"form": {
"wisp": "fuego fatuo",
"banshee": "banshee",
"fairy": "hada",
"shade": "sombra"
},
"card": {
"unknownMedium": "médium desconocido",
"contacted_one": "contactado {{count}} vez",
"contacted_other": "contactado {{count}} veces",
"discoveredBy": "descubierto por {{name}} · {{date}}"
},
"contacts_one": "1 contacto",
"contacts_other": "{{count}} contactos",
"sightings_one": "1 avistamiento",
"sightings_other": "{{count}} avistamientos",
"discoveredBy": "primer contacto por {{name}}",
"discoveredAnon": "primer contacto sin registrar",
"discoveredAt": "ingresó al registro el {{date}}",
"persona": "persona",
"quotes": "enunciados registrados",
"voice": "parámetros de voz",
"detail": {
"loading": "invocando el dosier…",
"notReached": "este espíritu no ha sido alcanzado",
"unknownMedium": "médium desconocido",
"stats": {
"sightings": "avistamientos",
"contacts": "contactos",
"discoveredBy": "descubierto por",
"discoveredAt": "ingresó al registro"
},
"utterances": "enunciados conocidos",
"noUtterances": "aún no hay enunciados registrados",
"voiceSignature": "firma de voz",
"attemptContact": "intentar contacto"
},
"empty": "ningún espíritu coincide con este filtro — el registro guarda sus secretos",
"back": "← volver al códice",
"notFound": "este espíritu no figura en el registro"
}
}