Documentation
Parcourir la documentation

DocumentationÉcrire un widget

Le manifeste

manifest.json dit à PlayerVox OverCrow ce qu’est un widget avant que son code ne s’exécute : son identité et sa version, ses noms, les tailles qu’il accepte, les permissions qu’il demande et les lignes qu’il ajoute à son menu d’options. Vous l’écrivez à la main ; il est empaqueté tel quel.

Le fichier est un seul objet JSON strict. Une marque d’ordre des octets, une clé en double, des données après l’objet ou un champ inconnu le font refuser : PlayerVox OverCrow n’ignore aucun champ.

Un exemple complet

JSONtemplates/counter/manifest.json
{
  "schemaVersion": 1,
  "apiVersion": 1,
  "id": "{{id}}",
  "version": "0.1.0",
  "name": {
    "en": "{{name}}",
    "fr": "{{name}}"
  },
  "sizing": {
    "fit": "none",
    "preferred": { "width": 200, "height": 96 },
    "min": { "width": 140, "height": 72 },
    "max": { "width": 640, "height": 640 }
  }
}

overcrow-widget init écrit ce fichier ; {{id}} et {{name}} sont les valeurs qu’il remplit. Voici un manifeste avec des permissions et un menu :

JSONdocs/content/examples/countdown/manifest.json
{
  "schemaVersion": 1,
  "apiVersion": 1,
  "id": "nova.countdown",
  "version": "1.0.0",
  "name": { "en": "Countdown", "fr": "Compte à rebours" },
  "sizing": {
    "fit": "none",
    "preferred": { "width": 220, "height": 190 },
    "min": { "width": 180, "height": 160 },
    "max": { "width": 480, "height": 400 }
  },
  "permissions": {
    "storage": true
  },
  "wrapper": {
    "menu": [
      {
        "type": "toggle",
        "id": "show-rounds",
        "label": { "en": "Show finished rounds", "fr": "Afficher les tours terminés" },
        "icon": "circle-check",
        "default": true
      },
      {
        "type": "action",
        "id": "reset-rounds",
        "label": { "en": "Reset the rounds", "fr": "Remettre les tours à zéro" },
        "icon": "rotate-ccw",
        "visibleWhen": "show-rounds"
      }
    ]
  }
}

Champs

ChampTypeObligatoireSignification
schemaVersionentier de 1 à 1ouiVersion du document de manifeste.
apiVersionentier de 1 à 1ouiVersion de l’API des widgets ; elle sélectionne ce schéma.
idwidget IDouiID du widget, de 3 octets à 128 octets : au moins deux segments de [a-z0-9-] séparés par des points, chacun d’au plus 63 octets, sans - au début ni à la fin ; <pseudo>.<nom> avec le pseudo de l’éditeur, ou un domaine vérifié de l’éditeur, inversé, suivi d’un nom. com.playervox et com.playervox.* sont réservés aux paquets signés par PlayerVox.
versionversionouiSemVer 2.0.0 MAJOR.MINOR.PATCH[-PRERELEASE] sous sa forme canonique, d’au plus 64 octets ; les métadonnées de build sont refusées.
nameWidgetNameouiNom localisé du widget, affiché par l’hôte.
sizingSizingouiRègles de taille et d’ajustement appliquées par le cadre du widget.
vmVmRequestnonBudget demandé pour la VM ; absent, les valeurs par défaut s’appliquent.
permissionsPermissionsnonPermissions demandées ; absent, aucune.
wrapperwrappernonLignes du widget dans le menu d’options de l’hôte.
requireshost featuresnonSources de données de l’hôte, distinctes, sans lesquelles le widget ne peut pas fonctionner, comme le requires d’une ligne de wrapper.menu. Sur une machine à laquelle il en manque une, l’hôte ne démarre ni n’affiche le widget et conserve sa place ; le widget revient quand la source revient.

Identité et version

  • id est l’identité du widget partout : dans le catalogue, dans le profil de l’utilisateur, dans son stockage. Il s’écrit <pseudo>.<nom>, votre pseudo d’éditeur dans l’espace créateurs de PlayerVox OverCrow et un nom (nova.lol-timers), ou, une fois un domaine vérifié, ce domaine inversé et un nom (gg.nova.lol-timers). Un ID est définitif : il n’est jamais donné à un autre widget, même après la suppression du vôtre. nova, example, yourhandle et yourname sont des pseudos d’exemple sous lesquels personne ne peut publier, et les ID sous com.playervox sont réservés aux widgets publiés par PlayerVox.
  • version est une version sémantique. Une version publiée ne change jamais : toute modification d’un widget publié est une nouvelle version, supérieure.
  • schemaVersion et apiVersion valent tous deux 1.

Noms

name donne le nom que PlayerVox OverCrow affiche dans sa liste de widgets et dans ses menus, en anglais et en français. Les deux sont obligatoires. Un nom s’affiche tel qu’il est écrit : ni caractère de contrôle, ni caractère invisible ou de sens d’écriture, comme U+200B (espace sans chasse) ou U+202E (forçage de droite à gauche). Les libellés du menu suivent la même règle.

ChampTypeObligatoireSignification
enname textouiNom en anglais.
frname textouiNom en français.

Tailles

sizing indique la taille du widget quand l’utilisateur l’ajoute, jusqu’où celui-ci peut le réduire et l’agrandir, et si PlayerVox OverCrow peut ajuster son cadre à son contenu. Les longueurs sont en pixels logiques, à une échelle de 100 % ; l’échelle du contenu choisie par l’utilisateur (de 50 % à 175 %) les multiplie.

ChampTypeObligatoireSignification
fitnone | both | heightouiAjustement au contenu que le widget accepte : sur les deux axes (both), en hauteur seulement (height) ou aucun (none). Le redimensionnement manuel reste toujours possible.
defaultModefit | manualnonMode d’un widget nouvellement placé ; par défaut fit quand fit n’est pas none, sinon manual. fit avec fit: none est refusé.
preferredDimensionsouiTaille initiale ; entre min et max.
minDimensionsouiPlus petite taille.
maxDimensionsouiPlus grande taille.

preferred, min et max sont chacun une paire de dimensions, avec min ≤ preferred ≤ max sur les deux axes :

ChampTypeObligatoireSignification
widthentier de 1 à 4096ouiPixels logiques.
heightentier de 1 à 4096ouiPixels logiques.

Avec fit: "both", PlayerVox OverCrow donne au cadre la taille dont la vue a besoin, dans les deux directions : l’Horloge grandit quand l’utilisateur active les secondes. Avec fit: "height", l’utilisateur choisit la largeur et la hauteur suit le contenu. Avec fit: "none", le cadre garde la taille que l’utilisateur lui a donnée et la vue le remplit. L’utilisateur peut toujours redimensionner à la main.

JSONwidgets/clock/manifest.json
  "sizing": {
    "fit": "both",
    "preferred": { "width": 110, "height": 60 },
    "min": { "width": 40, "height": 24 },
    "max": { "width": 480, "height": 240 }
  },

Permissions

Un widget n’obtient rien qu’il n’ait déclaré ici, et une déclaration n’accorde rien à elle seule : l’utilisateur donne d’abord son accord, dans le Centre de contrôle. Services et permissions explique chaque permission et ce qu’elle autorise.

ChampTypeObligatoireSignification
networkliste de NetworkRule ≤ 32nonRègles réseau, distinctes.
storagebooléennonStockage clé-valeur tenu par l’hôte ; false par défaut.
clipboardWritebooléennonÉcriture de texte dans le presse-papiers ; false par défaut.
gameEventsliste de game event ≤ 32nonNoms d’événements overcrow.game.<name>.v1, distincts.
capabilitiescapability namesnonCapabilities du schéma, distinctes.
JSONwidgets/stopwatch/manifest.json
  "permissions": {
    "capabilities": ["stopwatch.read", "stopwatch.control"]
  }

Une règle réseau est un objet à part entière ; ses champs sont décrits avec les règles réseau.

Le menu d’options

Chaque widget a un menu d’options, ouvert depuis son cadre. PlayerVox OverCrow y place d’abord ses propres lignes : opacité, taille du contenu, ajustement au contenu et, si le widget en a besoin, son compte. La visibilité en mode passif est l’œil de la barre d’outils du widget, pas une ligne du menu. wrapper.menu ajoute les vôtres en dessous : bascules, curseurs, choix, actions et groupes, chacun avec un libellé dans les deux langues.

Le menu prend la largeur de sa ligne la plus large, de 248 à 320 points. Au-delà, un libellé se termine par « … » et s’affiche en entier dans une infobulle ; un choix montre sa valeur sur 120 points au plus. Gardez des libellés courts, dans les deux langues.

JSONwidgets/clock/manifest.json
  "wrapper": {
    "menu": [
      {
        "type": "toggle",
        "id": "show-seconds",
        "label": { "en": "Show seconds", "fr": "Afficher les secondes" },
        "default": false
      },
      {
        "type": "toggle",
        "id": "show-date",
        "label": { "en": "Show date", "fr": "Afficher la date" },
        "default": true
      },
      {
        "type": "choice",
        "id": "date-format",
        "label": { "en": "Date format", "fr": "Format de date" },
        "visibleWhen": "show-date",
        "choices": [
          { "value": "day-month-year", "label": { "en": "dd/mm/yyyy", "fr": "jj/mm/aaaa" } },
          { "value": "year-month-day", "label": { "en": "yyyy-mm-dd", "fr": "aaaa-mm-jj" } },
          { "value": "month-day-year", "label": { "en": "mm/dd/yyyy", "fr": "mm/jj/aaaa" } }
        ],
        "default": "day-month-year"
      }
    ]
  }

PlayerVox OverCrow dessine les lignes, enregistre leurs valeurs dans le profil de l’utilisateur et les donne à la logique : lisez une valeur avec option(id, fallback) et suivez les changements avec onHost. Une ligne action n’a pas de valeur : elle appelle le gestionnaire que vous avez enregistré avec onMenu. Voir le menu d’options dans la logique.

Une ligne du menu peut fixer l’une de ses valeurs déclarées ou envoyer son action, rien d’autre. Elle ne peut ni accorder une permission ni ouvrir un lien, et choisir une ligne n’est pas une action de l’utilisateur qui autorise un appel de service protégé.

Champs communs à toutes les lignes

ChampTypeObligatoireSignification
typetoggle | slider | choice | action | groupouiType de la ligne.
ididentifiant ≤ 48 octetsouiUnique parmi toutes les lignes du widget ; clé de la valeur enregistrée.
labelMenuLabelouiLibellé localisé de la ligne.
iconnom d’icône LucidenonIcône placée devant le libellé, prise dans la liste de l’hôte et dessinée par lui.
visibleWhenidentifiant ≤ 48 octetsnonID d’une ligne toggle déclarée plus haut ; cette ligne n’est affichée que tant que cette bascule est activée.
requiresfps | telemetry.cpuTemperature | telemetry.gpuTemperaturenonSource de données de l’hôte ; l’hôte masque la ligne quand il ne peut pas la fournir.

Une ligne dotée de visibleWhen ne s’affiche que tant que la bascule nommée est activée : le format de date de l’Horloge apparaît quand la date est affichée. Une ligne dotée de requires est masquée sur une machine qui ne peut pas fournir cette donnée, comme les lignes de température du widget Performances :

JSONwidgets/performance/manifest.json
            "requires": "telemetry.cpuTemperature",

Une ligne group ouvre un sous-menu latéral avec ses propres lignes ; le widget Performances range ses cinq bascules de mesures sous une seule ligne :

JSONwidgets/performance/manifest.json
        "type": "group",
        "id": "shown-values",
        "label": { "en": "Shown values", "fr": "Valeurs affichées" },
        "rows": [

Types de ligne

toggle

Bascule booléenne.

ChampTypeObligatoireSignification
defaultbooléenouiValeur initiale.

slider

Nombre borné ; step > 0, step ≤ max - min, au plus 10000 pas, default dans l’intervalle.

ChampTypeObligatoireSignification
minnombre de -1000000 à 1000000ouiBorne inférieure.
maxnombre de -1000000 à 1000000ouiBorne supérieure, plus grande que min.
stepnombre de -1000000 à 1000000ouiPas.
defaultnombre de -1000000 à 1000000ouiValeur initiale.

choice

Une valeur parmi les choix déclarés.

ChampTypeObligatoireSignification
choicesliste de MenuChoice ≤ 16ouiAu moins 2 choix.
defaultidentifiant ≤ 48 octetsouiValeur de l’un des choix déclarés.

action

Appelle le gestionnaire enregistré par onMenu avec l’ID de la ligne. Ce n’est pas une action de l’utilisateur.

group

Sous-menu latéral qui contient des lignes autres que des groupes.

ChampTypeObligatoireSignification
rowsliste de MenuRow ≤ 16ouiAu moins une ligne ; un group n’y est pas admis.

Libellés et choix

Un label est un objet qui porte les deux langues :

ChampTypeObligatoireSignification
enlabel textouiLibellé en anglais.
frlabel textouiLibellé en français.

Chaque entrée de choices est une valeur et son libellé :

ChampTypeObligatoireSignification
valueidentifiant ≤ 48 octetsouiValeur enregistrée ; unique dans la ligne.
labelMenuLabelouiLibellé localisé du choix.

Sources de données indispensables

Au premier niveau du manifeste, requires liste les sources de données sans lesquelles le widget ne peut pas fonctionner, avec les mêmes noms que le requires d’une ligne. Sur une machine à laquelle il en manque une, PlayerVox OverCrow ne démarre pas le widget, ne l’affiche pas et conserve sa place dans la disposition ; le widget revient quand la source revient. Utilisez-le pour un widget qui n’a rien d’autre à afficher, comme un compteur d’images par seconde sans source de fréquence d’images. Pour une seule valeur facultative, préférez le requires d’une ligne.

Un budget de mémoire plus grand

La logique s’exécute avec un tas de 16 Mio. Un widget qui a besoin de plus le demande avec vm ; aucun autre budget de la VM ne peut être modifié.

ChampTypeObligatoireSignification
heapMiBentier de 16 à 48ouiPlafond du tas QuickJS en Mio, de 16 Mio à 48 Mio. Le plafond du processus de la VM (64 Mio) ne change pas.

Le plafond de mémoire du processus du widget reste le même : un tas plus grand laisse donc moins de place à tout le reste. Ne le demandez que si overcrow-widget dev montre que le widget s’arrête avec resource_limit.

Comment le manifeste est vérifié

overcrow-widget check valide le manifeste avec le validateur que PlayerVox OverCrow exécute lui-même, et désigne le membre qu’il refuse :

TEXT
error[manifest.sizing]: `sizing` is out of bounds or inconsistent
  --> manifest.json:7:3
   |
 7 |   "sizing": {
   |   ^
   = help: `min` <= `preferred` <= `max`, each within the schema's widget size bounds
check: 1 error(s), 0 warning(s)

PlayerVox OverCrow le valide de nouveau quand le paquet est admis dans le catalogue, quand il est installé et à chaque démarrage du widget. Une nouvelle version dont le manifeste demande plus que la précédente (une règle réseau, un événement de jeu, une capability, le stockage ou le presse-papiers) attend que l’utilisateur l’approuve dans le Centre de contrôle avant de remplacer la version installée.