Communitygithub.com

xbmaxx/hermes-intro-cinematic

Utilisez cette compétence pour ajouter un cinématique de démarrage personnalisé à Hermes Desktop.

Qu'est-ce que hermes-intro-cinematic ?

Cette compétence modifie le comportement du film d'introduction d'Hermes Desktop, le faisant jouer à chaque lancement au lieu d'une seule fois, et permet d'ajouter une piste audio personnalisée superposée aux effets sonores intégrés. Elle modifie trois fichiers clés : le store d'éligibilité, le composant de superposition et le processus principal Electron, en veillant à ce que le film ne boucle pas indéfiniment grâce à un indicateur `playedThisSession`. Elle fournit également des instructions détaillées pour superposer une piste vocale via un élément `<audio>` (évitant les problèmes CORS liés à `fetch` + `decodeAudioData`) et pour reconstruire et signer l'application après les modifications. Enfin, elle propose une méthode de vérification dans un navigateur ordinaire sans Electron, ainsi que des tests contractuels pour valider les changements. La compétence est conçue pour les développeurs qui souhaitent personnaliser l'expérience de démarrage d'Hermes Desktop tout en évitant les pièges courants comme les boucles infinies ou les désynchronisations audio.

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

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Cinématique d'Introduction Hermes

Hermes Desktop livre déjà un film d'introduction de 22 secondes — sept temps, un viewport cube en direct, des agents parallèles, la marque Nous — avec un son synthétisé. Il existe, mais presque personne ne le voit deux fois : l'éligibilité est guestOnboardingEnabled && !seen.

Cette compétence fait jouer le film à chaque lancement et vous permet de superposer votre propre piste vocale sur les signaux intégrés.

Quand l'utiliser

  • « Faire en sorte que l'animation de démarrage d'Hermes se joue à chaque fois. »
  • « Mettre mon audio sur l'animation de démarrage — superposé, sans remplacer les effets sonores. »
  • Toute modification dans apps/desktop/src/components/intro-reveal/.

Ce qu'est déjà le film

ÉlémentChemin
Porte (quand il joue)apps/desktop/src/components/intro-reveal/index.tsx
Éligibilité + store de phaseapps/desktop/src/store/intro-reveal.ts
Superposition bootsrc/components/intro-reveal/intro-root.tsx
Horloge, score, canvasuse-intro-clock.ts, timeline.ts, viewport-cube.ts
Signaux synthétiséssound.ts
Fenêtre nativeelectron/intro-reveal-window.ts, electron/main.ts

Calcul des temps : INTRO_TOTAL_MS = 17 600 est le temps de score ; le temps réel est × INTRO_PACE (1.25) = 22 000 ms, plus INTRO_EXIT_MS 900 ms → 23 125 ms au total.

Rejouer à chaque lancement — trois modifications, toutes requises

  1. src/store/intro-reveal.ts — isIntroRevealEnabled() ne doit plus exiger l'onboarding invité ; shouldPlayFirstRunIntro() retourne !playedThisSession en dehors de l'onboarding. Un indicateur module playedThisSession est obligatoire : l'effet de la porte se relance à chaque fois que la phase revient à 'hidden', ce qui est exactement ce que fait la fin du film — sans l'indicateur, il boucle indéfiniment.
  2. src/components/intro-reveal/intro-root.tsx — la fenêtre de superposition vérifie isOnboardingEnabled() et retournerait avant de peindre. Remplacez-la par le même prédicat que le moteur de rendu principal.
  3. electron/main.ts — introRevealController est construit avec enabled: GUEST_ONBOARDING, donc le processus principal refuse open. Mettez-le à true ; la vivacité est décidée dans le moteur de rendu.

beginOnboardingFlow() / queueGuideAfterIntro() / takeGuideShape() de l'onboarding se protègent tous eux-mêmes avec isOnboardingEnabled(), donc ils ne font rien lorsque l'onboarding est désactivé — aucune modification supplémentaire nécessaire.

Interrupteur de désactivation : localStorage['hermes-intro-reveal-always-v1'] = '0', ou lancez avec HERMES_SKIP_INTRO=1 / --skip-intro.

Superposer une piste audio

Placez un fichier dans apps/desktop/public/intro-voiceover.mp3 (vite copie public/ dans dist/ à l'identique), puis dans sound.ts :

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

— créez un élément Audio, réglez volume, démarrez-le, et setTimeout(stop, cutAtMs - 300) pour que les 300 ms finales diminuent progressivement au lieu de claquer. Appelez-le à côté de startPad() dans use-intro-clock.ts et arrêtez-le dans le nettoyage de l'effet.

Utilisez un élément <audio>, jamais fetch() + decodeAudioData(). Le moteur de rendu packagé tourne sur file://, où fetch et decodeAudioData sont bloqués par CORS ; les éléments média chargent correctement les actifs file://.

Les deux pistes sont indépendantes par construction — l'audio des éléments et les signaux WebAudio se mélangent au niveau du système d'exploitation. Rien ne diminue rien. Câblez $hapticsMuted aux deux, et abonnez-vous plutôt que de le lire une seule fois, sinon le basculement de sourdine se désynchronise.

Point de coupure :

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

Livrer dans une application packagée

Les modifications dans le code source ne font rien tant que l'asar n'est pas reconditionné. Deux pièges ici, qui valent la peine d'être vérifiés :

Trouvez l'application que l'utilisateur lance réellement — ne supposez jamais /Applications.

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

Les mises à jour d'Hermes atterrissent dans ~/.hermes/hermes-agent*/apps/desktop/release/mac-arm64/Hermes.app et le Dock pointe souvent là, tandis que /Applications/Hermes.app peut être une copie obsolète 0.0.0. Confirmez avec CFBundleShortVersionString (0.17.x est réel, 0.0.0 ne l'est pas).

Rafraîchissez aussi app.asar.unpacked/dist/. Les builds packagés listent dist/** dans asarUnpack, et resolveRendererIndex() préfère cette copie — reconditionner le asar seul et l'arborescence dépaquetée obsolète l'éclipse.

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     # les modifications du processus principal nécessitent aussi cette étape

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 est livré signé ad-hoc

--ignore-scripts est important sur les réseaux lents : le téléchargement du binaire Electron n'est pas nécessaire pour construire ou reconditionner, seulement pour exécuter le développement.

Vérifier sans Electron

Rendre le film dans un navigateur classique — sans packaging, sans téléchargement de binaire :

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())")

L'appel de montage de la route lui-même ne fait rien sans le pont ; l'import manuel après le bouchage est ce qui peint réellement. Pour vérifier la piste audio, bouchonnez window.Audio et enregistrez les instances — assurez-vous que currentTime avance avec le film et que paused === true, volume === 0 au point de coupure.

Tests contractuels qui valent la peine d'être conservés dans src/store/intro-reveal.test.ts :

  • joue lors d'un lancement normal même lorsque hermes-intro-reveal-seen-v1 est défini
  • joue exactement une fois — finishIntroReveal() ne doit pas le réarmer
  • respecte la clé de désactivation et le drapeau de lancement skipIntro
  • laisse les sémantiques de première exécution de l'onboarding intactes

Pièges

  • Supprimer playedThisSession donne une boucle de relecture infinie, pas un lancement sans bug.
  • INTRO_TOTAL_MS est le temps de score. Oublier INTRO_PACE coupe la voix off 4,4 s trop tôt.
  • Le drapeau enabled du processus principal et la garde de la superposition sont des portes séparées ; en manquer une ne rend rien, sans message d'erreur.
  • Quatre couches indépendantes maintiennent l'écran de retour (fermeture normale, Échap + 1,2 s de secours, deadman local, chien de garde du processus principal 34 s). N'en retirez pas une en modifiant les temps.
  • hermes update remplace l'application par la build officielle. Le correctif persiste dans git ; relancez l'installateur après la mise à jour.

Skills associés