Skip to content

Exemple complet — appli compilée avec Vite ​

Une carte affiche un message de préférence pendant trois secondes, puis termine sans récompense. Ce projet illustre sources → build Vite → configurations HUB → runtime servi. Il reprend le principe du scaffold Vite du HUB, avec une seule entrée et sans plugin facultatif. Il ne remplace pas les fichiers d’une appli existante : utiliser un workspace dédié ou intégrer les changements explicitement.

Créer les fichiers ci-dessous, installer les dépendances (npm install, puis conserver le lockfile), lancer npm run build, puis transpile dans le HUB. Pour une installation reproductible suivante, utiliser npm ci. La version de Vite est celle du scaffold consulté ; utiliser les versions validées par le projet, sans ajouter un deuxième bundler à une appli déjà équipée.

Les entrées configs/*.config.js sont compilées par le HUB, pas par Vite. Le build vide dist : toujours recompiler les configurations après chaque build. Le HUB recopie infos.json dans le runtime lors de la synchronisation des configurations et de la préparation de la comparaison/push, pas pendant transpile seul. Synchroniser les configurations depuis le HUB et contrôler le manifeste avant la preview. Le ZIP publié contient directement index.html, assets/, config/, les guides et infos.json.

package.json ​

json
{
  "name": "kappapps-compiled-card",
  "private": true,
  "version": "1.0.0",
  "scripts": {"build": "vite build"},
  "devDependencies": {"vite": "6.4.3"}
}

vite.config.mjs ​

js
export default {
    base: './',
    publicDir: 'public',
    build: {outDir: 'dist', emptyOutDir: true},
};

cli-config.json ​

json
{
  "runtime": {"root": "dist"},
  "configCompiler": {
    "entries": [
      "configs/preferences.config.js", "configs/data.config.js",
      "configs/default_triggers.config.js", "configs/historics.config.js",
      "configs/player_stats.config.js", "configs/trigger_arguments.config.js"
    ],
    "watchIncludes": ["configs/**"],
    "watchExcludes": []
  },
  "upload": {"excludes": [".*", "**/.*", "node_modules/**"]}
}

infos.json ​

json
{
  "app-index": "index.html",
  "externable": false,
  "display-type": 0,
  "overlay-formats": ["16:9", "9:16"],
  "streamer-readme": "streamer.md",
  "viewers-readme": "viewers.md",
  "thumbnail": null,
  "textset": null,
  "public-assets": null,
  "viewers-plugin": null,
  "streamer-plugin": null,
  "required": [],
  "scopes": [],
  "services": []
}

index.html ​

html
<!doctype html>
<html lang="fr">
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Carte compilée</title></head>
<body>
  <main id="card"><p id="status" role="status">Chargement…</p></main>
  <script type="module" src="./src/main.js"></script>
</body>
</html>

src/main.js ​

js
import './style.css';

const status = document.getElementById('status');
let closed = false;
let finishTimer;
const sdk = document.createElement('script');
const loadingTimer = setTimeout(failed, 15000);

function failed() {
    if (closed) return;
    closed = true;
    clearTimeout(loadingTimer);
    document.removeEventListener('AppliLoaded', start);
    status.textContent = 'Chargement impossible. Vérifier la connexion puis relancer.';
}

function start() {
    if (closed) return;
    clearTimeout(loadingTimer);
    kapp.onBeforeExit('*', () => {
        closed = true;
        clearTimeout(finishTimer);
    });
    status.textContent = kapp.preferences.get('message') || 'Bonjour !';
    kapp.display();
    finishTimer = setTimeout(() => {
        if (!closed) void kapp.finish().catch(error => console.error('Fin non confirmée', error));
    }, 3000);
}

document.addEventListener('AppliLoaded', start, {once: true});
sdk.onerror = failed;
sdk.src = 'https://kappapps.app/overlay/app/appli-2.0.js';
document.head.appendChild(sdk);

src/style.css ​

css
html, body { margin: 0; width: 100%; height: 100%; background: transparent; }
body { display: grid; place-items: center; font: 600 42px system-ui, sans-serif; }
#card { box-sizing: border-box; width: min(80%, 900px); padding: 48px; border-radius: 24px; background: #171c31; color: #fff; text-align: center; overflow-wrap: anywhere; }
#status { margin: 0; }

configs/preferences.config.js ​

js
module.exports = {type: 'group', groupItems: {
    message: {type: 'string', label: {fr: 'Message', en: 'Message'}, default: 'Bonjour !'},
}};

configs/data.config.js ​

js
module.exports = {};

configs/default_triggers.config.js ​

js
module.exports = [];

configs/historics.config.js ​

js
module.exports = [];

configs/player_stats.config.js ​

js
module.exports = [];

configs/trigger_arguments.config.js ​

js
module.exports = [];

public/streamer.md ​

markdown
# Carte compilée — guide streamer
Régler le champ Message dans les préférences, puis lancer l'appli sur un overlay actif.
Le message s'affiche trois secondes et l'appli se termine. L'arrêt manuel est possible depuis Kappapps.
Formats : horizontal et vertical. Aucun plugin, message Twitch, coût, récompense ou état de partie persistant.
Si le chargement échoue, vérifier la connexion et les fichiers du runtime avant de relancer.

public/viewers.md ​

markdown
# Carte compilée — guide viewers
Cette carte est déclenchée par le streamer et s'affiche trois secondes.
Elle ne demande aucune participation et ne distribue pas de récompense.

Recette ​

Après build et transpile, vérifier les six JSON, les deux guides et infos.json, puis servir dist. Le HTML produit référence un bundle haché sous assets/ ; aucun /src/main.js ne doit être demandé. Les chemins des guides du manifeste ne comportent ni public/ ni dist/.

Dans la preview Kappapps : vérifier le message configuré, le format horizontal puis vertical, la fermeture après trois secondes et un arrêt manuel avant cette échéance. Une erreur de script SDK doit laisser un message de chargement échoué dans la page ; aucune relance automatique n’est prévue. Une carte rendue dans un navigateur avec un SDK doublé ne prouve pas la fermeture via le core réel.

Tester aussi l’ordre inverse une fois dans une copie : transpile puis build efface config/. Refaire transpile répare le runtime ; ne jamais pousser la sortie intermédiaire. Pour une application réelle, compléter la livraison, ses langues et ses autres surfaces.