Appearance
kapp.preferences (config streamer)
Les préférences sont la configuration de l'appli définie par le streamer dans son dashboard. Tu déclares leur structure dans preferences.config.js, et tu les lis à l'exécution via kapp.preferences (ou le global prefs).
Lire les préférences
kapp.preferences.get(path = null): Record<string, any> | undefined— lit une préférence par chemin ('global.theme.color'). Renvoieundefinedsi le chemin n'existe pas. Sans argument : renvoie tout l'arbre.kapp.preferences.preferences— l'arbre complet (objet). Exposé aussi en globalprefs.
js
const color = kapp.preferences.get('global.theme.color') ?? '#9146FF';
// équivalent pratique : const color = prefs.global?.theme?.color ?? '#9146FF';Écrire les préférences (avancé)
await kapp.preferences.set(changes: { path: string, data: any }[]): Promise<{ changeset, errors }>— applique une liste de modifications, persistées côté serveur. ÉmetEVENTS.UPDATEDaprès sync.
js
const { errors } = await kapp.preferences.set([{ path: 'global.counter', data: 0 }]);
if (errors.length) console.warn('prefs non appliquées', errors);Déclarer la structure : preferences.config.js
Arbre de groupes et d'items typés, exporté en module.exports. Compilé en config/preferences.config.json par le HUB (transpile / watch). Réutilise des structures avec modules/Model.js.
js
const { Model } = require('./modules/Model');
module.exports = {
type: 'group',
appearance: { wrap: 'tabs' },
groupItems: {
global: {
type: 'group',
label: { en: 'Global', fr: 'Global' },
groupItems: {
color: {
type: 'string',
label: { en: 'Theme color', fr: 'Couleur du thème' },
},
},
},
},
};Type spécial : runTarget (cible lançable)
Un item runTarget affiche au streamer un picker mixant les applis oneshot installées et les widgets de son channel. La valeur lue (get(path) / prefs) est directement une cible pour kapp.apps.run() : { type: 'app', slug } | { type: 'widget', id } | null (rien de choisi). set() accepte le même objet. wuOptions.targets: 'app' | 'widget' restreint le picker ; default interdit ; en collection → tableau de cibles. Détails et exemples : app-to-app.md.
Type spécial : mediaFile (média du streamer ou de la bibliothèque publique)
Un item mediaFile affiche au streamer un picker de média : ses propres fichiers ou un asset de la bibliothèque publique Kappapps (référencé directement, sans copie). wuOptions : allowedTypes: ['sound'|'video'|'image'], withPosition: bool.
La valeur lue (get(path) / prefs) est hydratée en { file, volume, position } — passe-la telle quelle à kapp.medias.playMedia(pref), quel que soit le média choisi (channel ou bibliothèque).
Default : uniquement un asset de la bibliothèque publique, référencé par son slug — jamais un média de channel (aucun sens cross-channel) :
js
alertSound: {
type: 'mediaFile',
label: { en: 'Alert sound', fr: 'Son d\'alerte' },
wuOptions: { allowedTypes: ['sound'] },
default: { library: 'kapp-ding', volume: 70 },
},Le slug se trouve dans le catalogue (kapp.medias.library(), champ slug). Un slug inexistant = erreur CRIT à la validation de l'appli ; un asset dépublié = WARN (il reste jouable, mais il n'apparaît plus au catalogue).
Type spécial : status (statut de stream du channel)
Un item status affiche au streamer un sélecteur de ses statuts de stream configurés (les mêmes que k.statuses). La valeur lue (get(path) / prefs) est hydratée en { id, name, keyword } — même forme que StatusData de k.statuses — ou null si rien n'est sélectionné, ou si le statut choisi a été supprimé depuis. default/defaults interdits ; pas de wuOptions.
js
favoriteStatus: {
type: 'status',
label: { en: 'Favorite status', fr: 'Statut favori' },
},Type spécial : announces (annonce multi-canal configurée par le streamer)
Un item announces affiche au streamer le formulaire d'annonce complet : messages, variations, et les tracks qu'il veut activer (chat, écran, voix, dashboard, Discord, services d'appli). wuOptions.tags déclare les tags #…# que ton appli fournira, pour qu'il sache quoi écrire.
Ne lis pas la valeur pour la rejouer toi-même — donne son chemin au service d'annonces :
js
// preferences.config.js
messages: {
type: 'group',
groupItems: {
onWin: {
type: 'announces',
label: { en: 'On win', fr: 'À la victoire' },
wuOptions: { tags: { user: 'Le gagnant', amount: 'Les points gagnés' } },
},
},
},js
// script.js
await kapp.announces.play('messages.onWin', { user: user.getDisplayName(), amount: '250' });Scope à déclarer dans infos.json : announces:play — et lui seul, quelles que soient les tracks que le streamer activera. La valeur hydratée porte une référence interne (_ref) et un discord.webhook réduit à un booléen : le webhook du streamer ne sort jamais du serveur.
appearance : options de présentation d'un item
Chaque item (groupe, collection ou champ) accepte un objet appearance qui pilote son rendu dans le dashboard streamer :
| Clé | Valeurs | Effet |
|---|---|---|
wrap | 'tabs' | 'list' (défaut) | Sur un group ou une collection : affiche les enfants en onglets plutôt qu'empilés. |
tabsStyle | 'underline' | 'pills' | Sur un group en wrap: 'tabs' : force le style des onglets. Par défaut le style est automatique (underline pour le groupe racine, pills pour les groupes imbriqués) — c'est la hiérarchie visuelle recommandée. |
choicesStyle | 'cards' | 'select' (défaut) | Sur un multipleChoice à choices statiques (objet) : affiche les options en cartes cochables au lieu d'un <select multiple>. Ignoré (WARN) si choices est un path dynamique. |
color | — | Déprécié, ignoré : la colorisation par appli n'est plus rendue, tous les items utilisent l'accent neutre du design system. La clé reste tolérée dans les schémas existants ; ne plus la déclarer dans une nouvelle appli. |
folded | true | false | L'item démarre replié (dépliable via sa poignée ou son titre). |
hidden | true | false | Item masqué dans le formulaire (la valeur reste lue/écrite normalement). |
handle | true | false | Affiche la poignée de pli/tri à gauche de l'item. |
lineBreak | true | false | Insère un séparateur horizontal avant l'item ; lineBreakText: '...' y ajoute un texte. |
form | — | Déprécié, ignoré : le formulaire utilise un layout unique (label au-dessus du champ). |
js
settings: {
type: 'group',
label: { en: 'Advanced', fr: 'Avancé' },
appearance: { folded: true, handle: true },
groupItems: { /* ... */ },
},Pièges
prefsn'est pas réactif : après unset(), relis la valeur (ou écouteEVENTS.UPDATED).- N'édite pas
config/preferences.config.jsonà la main : il est régénéré depuis le.js. get(path)renvoieundefinedpour un chemin inconnu → prévois des valeurs par défaut.