Communitygithub.com

xbmaxx/hermes-intro-cinematic

Utiliza esta habilidad para agregar un cinemático de inicio a Hermes Desktop y superponer tu propia pista de voz.

¿Qué es hermes-intro-cinematic?

Esta habilidad modifica Hermes Desktop para que la película introductoria de 22 segundos (que normalmente solo se reproduce una vez para nuevos usuarios invitados) se reproduzca en **cada inicio**. También permite superponer una pista de voz personalizada sobre los efectos de sonido originales, sin reemplazarlos. Está diseñada para desarrolladores que trabajan con el código fuente de Hermes Desktop (TypeScript, React, Vite, Electron) y necesitan personalizar la experiencia de arranque. La implementación requiere modificar tres archivos clave para eliminar la restricción de "solo invitados", sincronizar la ventana de superposición y habilitar el controlador en el proceso principal. Para superponer audio, se coloca un archivo MP3 en la carpeta `public/` y se modifica `sound.ts` para crear un elemento `<audio>` que se inicia junto con los cues sintetizados. La habilidad incluye instrucciones detalladas para empaquetar la aplicación modificada, verificar en un navegador sin Electron y consejos para evitar problemas comunes como bucles infinitos, sincronización incorrecta de tiempos o sombras del empaquetado antiguo. Soporta desactivación mediante variables de entorno o localStorage.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add xbmaxx/hermes-intro-cinematic

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Cinemático de Inicio de Hermes

Hermes Desktop ya incluye una película introductoria de 22 segundos — siete compases, una vista de cubo en vivo, agentes paralelos, la marca Nous — con sonido sintetizado. Existe, pero casi nadie la ve dos veces: la elegibilidad es guestOnboardingEnabled && !seen.

Esta habilidad hace que la película se reproduzca en cada inicio y permite superponer tu propia pista de voz sobre los cues incorporados.

Cuándo usarlo

  • "Haz que la animación de inicio de Hermes se reproduzca cada vez."
  • "Pon mi audio en la animación de arranque — superpuesto, sin reemplazar los efectos de sonido."
  • Cualquier cambio en apps/desktop/src/components/intro-reveal/.

Qué ya es la película

PiezaRuta
Puerta (cuándo se reproduce)apps/desktop/src/components/intro-reveal/index.tsx
Elegibilidad + tienda de faseapps/desktop/src/store/intro-reveal.ts
Ventana de superposición de arranquesrc/components/intro-reveal/intro-root.tsx
Reloj, puntuación, lienzouse-intro-clock.ts, timeline.ts, viewport-cube.ts
Cues sintéticossound.ts
Ventana nativaelectron/intro-reveal-window.ts, electron/main.ts

Matemáticas de sincronización: INTRO_TOTAL_MS = 17 600 es tiempo de partitura; el tiempo real es × INTRO_PACE (1.25) = 22 000 ms, más INTRO_EXIT_MS 900 ms → 23 125 ms en total.

Reproducción en cada inicio — tres ediciones, todas necesarias

  1. src/store/intro-reveal.ts — isIntroRevealEnabled() debe dejar de requerir incorporación de invitados; shouldPlayFirstRunIntro() devuelve !playedThisSession fuera de incorporación. Una bandera playedThisSession a nivel de módulo es obligatoria: el efecto de la puerta se vuelve a ejecutar cada vez que la fase vuelve a 'hidden', que es exactamente lo que hace terminar la película — sin la bandera, se repetiría infinitamente.
  2. src/components/intro-reveal/intro-root.tsx — la ventana de superposición se protege con isOnboardingEnabled() y devolvería antes de pintar. Cámbiala al mismo predicado que el renderizador principal.
  3. electron/main.ts — introRevealController se construye con enabled: GUEST_ONBOARDING, por lo que el proceso principal rechaza open. Ponlo en true; la vivacidad se decide en el renderizador.

beginOnboardingFlow() / queueGuideAfterIntro() / takeGuideShape() de la incorporación se autoprotegen con isOnboardingEnabled(), por lo que no hacen nada cuando la incorporación está desactivada — no se necesita cambio adicional.

Interruptor de desactivación: localStorage['hermes-intro-reveal-always-v1'] = '0', o inicia con HERMES_SKIP_INTRO=1 / --skip-intro.

Superposición de una pista de audio

Coloca un archivo en apps/desktop/public/intro-voiceover.mp3 (vite copia public/ a dist/ textualmente), luego en sound.ts:

export function startVoiceover(cutAtMs: number): { stop: () => void }

— crea un elemento Audio, establece volume, inícialo, y setTimeout(stop, cutAtMs - 300) para que los últimos 300 ms disminuyan en lugar de cortar. Llámalo junto a startPad() en use-intro-clock.ts y detenlo en la limpieza del efecto.

Usa un elemento <audio>, nunca fetch() + decodeAudioData(). El renderizador empaquetado se ejecuta en file://, donde fetch y decodeAudioData están bloqueados por CORS; los elementos multimedia cargan assets file:// sin problemas.

Las dos pistas son independientes por construcción — el audio de elementos y los cues WebAudio se mezclan a nivel del sistema operativo. Nada se silencia mutuamente. Conecta $hapticsMuted a ambas, y suscríbete en lugar de leerlo una vez, o el interruptor de silencio se desincroniza.

Punto de corte:

const cutMs = Math.round((INTRO_TOTAL_MS + INTRO_EXIT_MS) * INTRO_PACE)  // 23 125

Envíalo a una aplicación empaquetada

Las ediciones del código fuente no hacen nada hasta que se vuelva a empaquetar el asar. Dos trampas aquí, ambas vale la pena revisar:

Encuentra la aplicación que el usuario realmente inicia — nunca asumas /Applications.

ps aux | grep '[H]ermes.app/Contents/MacOS/Hermes'
defaults read com.apple.dock persistent-apps | grep -o '/[^"]*Hermes\.app'

Las actualizaciones de Hermes llegan a ~/.hermes/hermes-agent*/apps/desktop/release/mac-arm64/Hermes.app y el Dock a menudo apunta allí, mientras que /Applications/Hermes.app puede ser una copia obsoleta 0.0.0. Confirma con CFBundleShortVersionString (0.17.x es real, 0.0.0 no lo es).

Actualiza también app.asar.unpacked/dist/. Las compilaciones empaquetadas listan dist/** en asarUnpack, y resolveRendererIndex() prefiere esa copia — reempaquetar solo el asar y el árbol desempaquetado obsoleto lo sombrea.

npm install --workspace apps/desktop --ignore-scripts \
  --engine-strict=false --min-release-age=0
cd apps/desktop && node ../../node_modules/vite/bin/vite.js build
node scripts/bundle-electron-main.mjs     # las ediciones del proceso principal también necesitan este paso

asar extract "$RES/app.asar" "$WORK"
rm -rf "$WORK/dist" && cp -R apps/desktop/dist "$WORK/dist"
asar pack "$WORK" "$WORK/app.asar"
mv -f "$WORK/app.asar" "$RES/app.asar"
cp -R apps/desktop/dist/. "$RES/app.asar.unpacked/dist/"
codesign --force --deep -s - "$APP"      # ad-hoc; Hermes se distribuye firmado ad-hoc

--ignore-scripts importa en redes lentas: la descarga del binario de Electron no es necesaria para construir o reempaquetar, solo para ejecutar dev.

Verificar sin Electron

Renderiza la película en un navegador simple — sin empaquetado, sin descarga de binario:

cd apps/desktop && node ../../node_modules/vite/bin/vite.js --host 127.0.0.1 --port 5174
page.add_init_script("""
window.hermesDesktop = {
  guestOnboardingEnabled: true, skipIntro: false,
  introReveal: { ready(){}, skip(){},
    open(){ return Promise.resolve() }, close(){ return Promise.resolve() },
    onSkip(){ return () => {} }, onClosed(){ return () => {} } }
};
""")
page.goto("http://127.0.0.1:5174/?win=intro")
page.evaluate("import('/src/components/intro-reveal/intro-root.tsx').then(m => m.mountIntroReveal())")

La propia llamada de montaje de la ruta no hace nada sin el puente; la importación manual después de simular es lo que realmente pinta. Para verificar la pista de audio, simula window.Audio y registra las instancias — afirma que currentTime avanza con la película y que paused === true, volume === 0 en el corte.

Pruebas de contrato que vale la pena mantener en src/store/intro-reveal.test.ts:

  • se reproduce en un inicio normal incluso cuando hermes-intro-reveal-seen-v1 está establecido
  • se reproduce exactamente una vez — finishIntroReveal() no debe rearmarla
  • respeta la clave de exclusión y la bandera de inicio skipIntro
  • deja intacta la semántica de primera ejecución de la incorporación

Errores comunes

  • Eliminar playedThisSession provoca un bucle de reproducción infinito, no un inicio sin errores.
  • INTRO_TOTAL_MS es tiempo de partitura. Olvidar INTRO_PACE corta la voz 4.4 s antes.
  • La bandera enabled del proceso principal y la propia protección de la ventana de superposición son puertas separadas; falla cualquiera y no se renderiza nada, sin error.
  • Cuatro capas independientes mantienen la pantalla regresando (cierre normal, Esc + 1.2 s de respaldo, deadman local, vigilante del proceso principal de 34 s). No elimines una mientras cambias la sincronización.
  • hermes update reemplaza la aplicación por la compilación oficial. El parche persiste en git; vuelve a ejecutar el instalador después de actualizar.

Skills relacionados