Ajouter vos propres commandes à la palette ⌘K de StormPress

Une commande StormPress se tape au clavier : on la cherche, on la choisit, c'est fait. Vous pouvez ajouter les vôtres avec une seule fonction, et elles suivent StormPress dans la palette du cœur de WordPress, là où elle est chargée, comme dans la sienne. Exemple complet : une commande /brouillons qui retrouve un brouillon par son titre et l'ouvre dans l'éditeur.

StormPress fournit des commandes slash : /publier, /extrait, /lier, /aller, /corbeille et /media (cette dernière seulement pour les comptes qui ont accès à la médiathèque). Leurs anciens noms anglais — /publish, /excerpt, /suggest-links, /go, /trash — restent acceptés comme alias.

Chaque équipe a ses gestes répétitifs ; vous pouvez en faire des commandes. L'API tient en une fonction : window.stormpress.registerCommand().

Où apparaissent vos commandes

⌘ K (Ctrl K hors Mac) n'ouvre pas la même palette selon l'écran. StormPress ne se fie pas au numéro de version : il regarde, écran par écran, si la palette du cœur y est chargée (le store core/commands).

  • Là où elle l'est — dans l'administration de WordPress 7.1, où nous l'avons constaté, et dans l'éditeur de blocs, qui la charge sur toutes les versions prises en charge par StormPress (6.4 et suivantes) — ⌘K lui appartient : StormPress n'ouvre pas de seconde palette, il inscrit ses commandes dans celle du cœur. Chacune y porte sa description suivie de « (StormPress) » et se trouve en tapant son nom sans la barre oblique, l'un de ses alias, ou « stormpress ». Une commande sans suggestions s'exécute aussitôt ; une commande à suggestions ouvre la palette StormPress directement dans son mode suggestion, le temps de choisir. StormPress y ajoute aussi ses propres écrans et, pour les comptes qui ont accès à la médiathèque, la recherche des médias, que la palette du cœur ne trouve pas d'elle-même.
  • Ailleurs — écrans où la palette du cœur n'est pas chargée, par exemple hors de l'éditeur sur des versions plus anciennes de WordPress — ⌘K ouvre la palette StormPress. Elle cherche aussi les articles et pages, les médias (pour les mêmes comptes), les écrans de StormPress et les réglages courants de WordPress ; une saisie qui commence par / n'y cherche que des commandes.

Vous n'avez pas à choisir : une commande enregistrée avec window.stormpress.registerCommand() est inscrite dans les deux, y compris quand votre script s'exécute après le chargement de StormPress.

Une première commande en dix lignes

window.stormpress.registerCommand( {
    name: '/bonjour',                // commence obligatoirement par « / »
    description: 'Dire bonjour',       // affichée et cherchée dans les palettes
    icon: 'Sparkles',                 // icône (facultatif, voir plus bas)
    category: 'Équipe',               // regroupement dans la palette StormPress (facultatif)
    run( ctx ) {
        ctx.toast( 'Bonjour !', 'success' );
    },
} );

Un nom sans / initial est refusé avec une erreur. Un nom déjà pris remplace la commande existante, avec un avertissement dans la console : préfixez les vôtres, par exemple /agence-….

La palette StormPress ne connaît que sept icônes : Send, Sparkles, Link, Trash2, FileText, Search et Image. Tout autre nom, ou l'absence de nom, affiche une flèche. La palette du cœur n'affiche ni l'icône ni la catégorie, seulement la description : rédigez-la comme une action complète.

Donner plusieurs noms à une commande

La propriété facultative aliases liste d'autres noms acceptés. C'est elle qui permet aux commandes intégrées de garder leur ancien nom anglais :

window.stormpress.registerCommand( {
    name: '/agence-rapport',
    aliases: [ '/agence-report' ],
    description: 'Ouvrir le rapport SEO',
    run( ctx ) {
        ctx.navigate( '/seo' );
    },
} );

Un alias se comporte comme le nom principal : la palette StormPress le trouve et l'exécute, la palette du cœur le reçoit comme mot-clé. La commande n'apparaît qu'une fois, sous son nom principal. Quelques règles :

  • un alias doit, lui aussi, commencer par / ;
  • un alias déjà utilisé par une autre commande, comme nom ou comme alias, est ignoré avec un avertissement ; la commande s'enregistre quand même ;
  • un nom enregistré explicitement l'emporte sur un alias homonyme : une extension qui enregistre encore /publish remplace l'alias de /publier, qui reste disponible sous son nom français ;
  • window.stormpress.unregisterCommand() accepte le nom ou l'un des alias, et retire la commande avec tous ses alias, y compris de la palette du cœur.

Ce que reçoit votre commande

La fonction run reçoit un objet ctx :

PropriétéRôle
ctx.argsLe texte saisi dans la palette StormPress au moment de l'exécution : en mode suggestion, la recherche tapée ; sinon, le texte qui a servi à trouver la commande (par exemple « /bonj »)
ctx.selectionLa suggestion choisie, si la commande propose une autocomplétion
ctx.confirm( message )Affiche une confirmation et renvoie une promesse true ou false
ctx.toast( message, type )Affiche une notification : 'success', 'error' ou 'info'
ctx.navigate( route )Ouvre un écran du back-office StormPress par sa route, par exemple '/seo'

run peut être asynchrone : la palette se ferme aussitôt, sans attendre la fin de la promesse. Pour signaler une opération longue, appelez ctx.toast( '…', 'info' ) au début. Dans les deux palettes, une erreur levée s'affiche en notification.

Une commande utile : retrouver un brouillon

Une commande peut proposer des suggestions pendant la frappe. Une fois choisie, la palette StormPress passe en mode suggestion : après chaque pause de frappe de 200 millisecondes, elle appelle votre fonction suggest avec la saisie ; Entrée exécute la commande avec la suggestion retenue. Voici /brouillons, qui cherche parmi les brouillons d'articles que vous pouvez modifier, via l'API REST de WordPress, et ouvre celui que vous choisissez.

( function () {
    // Les titres arrivent encodés (« L’été ») : on n'en garde que le texte.
    function texte( html ) {
        return new DOMParser().parseFromString( html, 'text/html' ).body.textContent;
    }

    function register() {
        var sp = window.__stormpress;
        // restUrl vise …/wp-json/stormeo/v1 : on en déduit la racine REST du site.
        var restRoot = sp.restUrl.replace( /stormeo\/v1\/?$/, '' );

        window.stormpress.registerCommand( {
            name: '/brouillons',
            aliases: [ '/drafts' ],
            description: 'Ouvrir un brouillon par son titre',
            icon: 'FileText',
            category: 'Contenu',

            async suggest( query, signal ) {
                var url = restRoot + 'wp/v2/posts?status=draft&per_page=8&_fields=id,title'
                    + ( query ? '&search=' + encodeURIComponent( query ) : '' );
                var res = await fetch( url, { headers: { 'X-WP-Nonce': sp.nonce }, signal: signal } );
                if ( ! res.ok ) return [];
                var posts = await res.json();
                return posts.map( function ( p ) {
                    return { label: texte( p.title.rendered ) || '(sans titre)', value: String( p.id ) };
                } );
            },

            run( ctx ) {
                if ( ! ctx.selection ) {
                    ctx.toast( 'Choisissez un brouillon dans la liste.', 'info' );
                    return;
                }
                // adminUrl se termine par « / » : …/wp-admin/
                window.location.assign( sp.adminUrl + 'post.php?post=' + ctx.selection.value + '&action=edit' );
            },
        } );
    }

    if ( window.stormpress ) {
        register();
    } else {
        window.addEventListener( 'DOMContentLoaded', register );
    }
} )();

Dans la palette du cœur, elle apparaît sous « Ouvrir un brouillon par son titre (StormPress) » ; la choisir ouvre la palette StormPress, prête à chercher. Quatre détails comptent :

  • L'authentification. L'en-tête X-WP-Nonce fait passer la requête avec la session de l'utilisateur connecté. Ses droits s'appliquent : la commande n'ouvre aucun accès que WordPress ne lui accorde déjà.
  • La légèreté. _fields=id,title et per_page=8 limitent la réponse à ce dont la palette a besoin. suggest est rappelée à chaque saisie : elle doit rester rapide.
  • L'abandon. Le second argument, signal, est abandonné dès que la saisie change. Transmis à fetch, il annule la requête devenue inutile ; la palette ignore l'erreur qui en résulte.
  • Les erreurs. Si le serveur répond par une erreur, suggest renvoie une liste vide : la palette affiche « Aucun résultat » et reste utilisable.

Charger le script depuis une extension

StormPress charge son registre de commandes dans l'administration par le script stormeo-app, un module JavaScript différé. Déclarez-le comme dépendance pour que WordPress place le vôtre après lui :

add_action( 'admin_enqueue_scripts', function () {
    if ( ! wp_script_is( 'stormeo-app', 'enqueued' ) ) {
        return; // StormPress absent ou désactivé
    }
    wp_enqueue_script(
        'monagence-commandes',
        plugins_url( 'commandes.js', __FILE__ ),
        [ 'stormeo-app' ],
        '1.0.0',
        [ 'strategy' => 'defer' ]
    );
}, 20 );

StormPress met son script en file à la priorité par défaut (10) : la priorité 20 garantit qu'il y est déjà au moment du test. Le garde if ( window.stormpress ) couvre le reste : si votre script s'exécute avant le registre, l'enregistrement attend la fin du chargement de la page.

Commandes ou outils du copilote ?

Les deux s'étendent, pour des usages différents :

  • une commande est un geste que vous déclenchez, en JavaScript, dans votre navigateur ;
  • un outil est une action que le copilote peut exécuter pour répondre à une demande, en PHP, sur le serveur.

Si l'action doit pouvoir être demandée en langage naturel (« programme ce guide pour lundi »), c'est un outil : voir notre tutoriel dédié. Si c'est un raccourci que vous lancez dix fois par jour, c'est une commande.

StormPress Lite est gratuit, sous licence GPL, et se désactive en un clic. Deux minutes pour l'essayer sur votre site.

Télécharger StormPress Lite