Aller au contenu

custom_dir

Fonctionnalités avancées

Utiliser le custom_dir permet de modifier le comportement du thème en profondeur ou d'en réutiliser certaines fonctionnalités.

Pour bien mettre à profit le custom_dir, il faut être familier avec la personnalisation d'un thème mkdocs.


"With great powers..."

Définir un custom_dir#

Il suffit d'ajouter un dossier Ă  la racine du projet, et d'ajouter son nom dans la section theme du fichier mkdocs.yml.
Typiquement, le nom de dossier est overrides, mais il est possible d'utiliser un autre nom aussi.

theme:
    custom_dir: overrides

Utilisation#

Surcharges de fichiers#

Il est possible d'ajouter autant de fichiers que nécessaire dans un custom_dir, du moment qu'ils n'ont pas les mêmes noms que ceux déclarés par les thèmes parents.

Si par contre il s'agit de modifier l'un des fichiers définis par un des thèmes parents, il faut identifier le thème qui définit le fichier concerné, récupérer le contenu du fichier à modifier, puis créer un fichier du même nom et chemin relatif par rapport au custom_dir que dans le dossier templates d'origine, et y faire les modifications souhaitées.

Il est possible de trouver les fichiers définis par les thèmes aux endroits suivants :

  • Dossier templates du thème (PMT)
  • Dossier templates de material.
  • Il est aussi possible de retrouver ces dossiers sur une installation locale, dans les rĂ©pertoires d'installation des thèmes.


Surcharge de main.html#

À partir de la version 3.2.0 du thème, il est possible de surcharger le fichier main.html du thème, comme cela se fait avec mkdocs-material, pour pouvoir injecter sa propre logique autour de celle du thème, voire pour modifier certains comportements du thème.

Pour cela, il suffit d'ajouter dans le custom_dir du projet un fichier main.html avec le contenu suivant :

{% extends "base_pmt.html" %}

Notez que le fichier à étendre est celui du thème, et non le fichier "base.html" provenant de mkdocs-material (et sur lequel s'appuie le thème lui-même).


Il est ensuite possible d'ajouter de la logique autour des différents blocs Jinja :

{% extends "base_pmt.html" %}

{% block scripts %}

<!-- Things to do before the original block... -->
{{ super() }}
<!-- Things to do after... -->

{% endblock %}

Modifier le JS de PMT#

Il est possible de modifier relativement facilement les classes utilisées dans la couche JS du thème pour y ajouter des nouveaux comportements ou les modifier.


Très exposé aux "breaking changes"

Modifier les comportements du thème implique d'écrire du code qui a beaucoup plus de chances de "casser" suite à des modifications internes du code du thème.

En effet, Pyodide-Mkdocs-Theme est développé avec en vue des utilisateurs travaillant pour l'essentiel avec une documentation markdown/mkdocs et ne personnalisant pas ou alors très peu le thème lui-même (en dehors des options proposées).
Ainsi, l'interface "externe" (les macros, la configuration du plugin) introduit le moins possible des breaking changes, mais ceux-ci sont nettement plus fréquents dans la machinerie interne s'ils permettent d'introduire diverses améliorations.


À partir de la version 4.0.0 du thème, le remplacement des classes du thème (Ide, Terminal, Qcm, ...) peut se faire en surchargeant le fichier js-scripts/overlord.js depuis le custom_dir. Pour cela :

  1. Créer un fichier overrides/js-scripts/overlord.js (ou équivalent si votre custom_dir n'est pas nommé overrides).
  2. Y ajouter le code suivant :

    import { waitForClassesPoolReady } from "functools";
    
    // Réexporte pour que ce module soit importable depuis subscriptions.js:
    export default waitForClassesPoolReady
    
    waitForClassesPoolReady(_=>{
        /* Cette fonction est appelée (sans argument) une fois que toutes les
           classes du thème nécessaires à la page en cours ont été enregistrées
           dans `CONFIG.CLASSES_POOL`.
           Vous pouvez les remplacer comme bon vous semble (à vos risques et périls...).
           Nota: l'objet CONFIG est disponible depuis l'environnement global.
         */
    
        return true
        /* Ne pas oublier de renvoyer true une fois vos remplacements effectués.
           Renvoyer undefined lèverait une erreur.
    
           Renvoyer une valeur "falsy" ferait que la fonction sera rappelée plus tard,
           jusqu'Ă  ce qu'elle finisse par renvoyer une valeur "truthy".
    
           Il faut impérativement qu'une valeur "truthy" soient renvoyée à un moment,
           sans quoi les éléments de la page ne seront jamais fonctionnels : le script
          `subscriptions.js` attend la résolution de cette étape pour faire son travail !
         */
    })
    

    Cette logistique permet de garantir que votre code sera exécuté au moment approprié malgré les importations asynchrones :

    • Après que toutes les classes nĂ©cessaires Ă  la page aient Ă©tĂ© enregistrĂ©es.
    • Avant que le thème n'ait créé les objets nĂ©cessaires au fonctionnement de la page Ă  partir des classes enregistrĂ©es dans CONFIG.CLASSES_POOL.
  3. Le modifier en créant vos propres classes.

    • Vos classes devraient Ă©tendre les classes existantes, pour garantir le bon fonctionnement de l'ensemble.
    • Avant d'Ă©tendre une classe, il faut vĂ©rifier si elle est bien dĂ©finie dans CONFIG.CLASSES_POOL : en effet, seules les classes utiles Ă  la page sont chargĂ©es, alors que le fichier overlord.js est exĂ©cutĂ© quel que soit le contenu de la page.


Voici un exemple d'utilisation simplifié à l'extrême, qui fait en sorte que tous les IDEs démarrent avec le message Yup! présent dans le terminal :

Contenu de overlord.js
import { waitForClassesPoolReady } from "functools";

// Réexporte pour que ce module soit importable depuis subscriptions.js:
export default waitForClassesPoolReady

waitForClassesPoolReady(_=>{

  // Si la classe est définie, on la remplace :
  if(CONFIG.CLASSES_POOL.Ide){

    // On étend la classe existante :
    class YupIde extends CONFIG.CLASSES_POOL.Ide {
      build(){
        super.build()
        this.terminalEcho('Yup!')   // Modification de comportement voulue...
      }
    }

    // On remplace la classe enregistrée par la nôtre:
    CONFIG.CLASSES_POOL.Ide = YupIde
  }

  // On n'oublie pas de "dire qu'on a terminé":
  return true
})

Résultat... Yup!



Remplacement des classes JS du thème (3.2.0 <= PMT < 4.0.0)

Pour utiliser vos propres classes JS à la place de celles du thème, il suffit de créer un fichier main.html et d'y modifier le bloc Jinja scripts avec un code utilisant la logique suivante:

Modifier les classes du thème
{% extends "base_pmt.html" %}

{% block scripts %}
<!--
Insérer ici une balise script ou un lien vers un fichier JS dont le code va
modifier les classes de `CONFIG.CLASSES_POOL` avant que les objets ne soient
instanciés dans la page (depuis le bloc `scripts` parent).

Exemple simple ci-dessous, créant tous les IDEs avec un message `"Yup!"`
affiché dès le démarrage dans les terminaux :
-->
<script>
  if(CONFIG.CLASSES_POOL.Ide){
    class YupIde extends CONFIG.CLASSES_POOL.Ide {
      build(){
        super.build()
        this.terminalEcho('Yup!')
      }
    }
    CONFIG.CLASSES_POOL.Ide = YupIde
  }
</script>

{{ super() }}

{% endblock %}


Résultat... Yup!

Remarques sur le code JS utilisé
  • La classe dĂ©clarĂ©e dans ce code Ă©tend la classe IdeRunner, stockĂ©e dans CONFIG.CLASSES_POOL.Ide. Ceci permet de modifier les comportements existants des IDEs.
    Notez qu'il est de loin préférable de leur ajouter des comportements que de modifier les comportements existants, ce qui est beaucoup plus propice à casser lors d'une mise à jour du thème.

  • Comme les scripts du thème ne sont chargĂ©s que s'ils sont utiles au contenu de la page, il faut vĂ©rifier que la classe a bien Ă©tĂ© dĂ©finie et stockĂ©e dans CONFIG.CLASSES_POOL avant d'Ă©crire le code l'Ă©tendant.

  • Une fois la classe personnalisĂ©e dĂ©clarĂ©e, elle doit remplacer la classe d'origine dans l'objet CONFIG.CLASSES_POOL afin d'ĂŞtre utilisĂ©e pour crĂ©er les objets dans la page.

Crochets html (obsolètes) #

Avant la version 3.2.0, surcharger le fichier main.html n'était pas possible. Un système de "fichiers crochets" avait donc été mis en place pour permettre une certaine flexibilité malgré tout.

Ces fichiers crochets sont toujours utilisables, mais leur présence sur le disque provoque un warning dans le terminal, lors des mkdocs build ou serve. Il est conseillé de les remplacer par la surcharge du fichier main.html du thème.


Utilisation des fichiers crochets (PMT < 3.2.0)

Afin de facilité la personnalisation du thème, sans avoir besoin de redéclarer des fichiers entiers, le thème met à disposition des fichiers "crochets" qui permettent d'insérer de la logique à différents moments, dans le code html de chaque page.


Pour utiliser ces crochets, il suffit d'ajouter au custom_dir un dossier hooks contenant le ou les fichiers html appropriés à votre usage :

Crochet Insère de la logique dans le fichier main.html...
hooks/libsBefore.html ...au début du bloc libs
hooks/libsAfter.html ...Ă  la fin du bloc libs
hooks/contentBefore.html ...au début du bloc content
hooks/contentAfter.html ...Ă  la fin du bloc content
hooks/scriptsBefore.html ...au début du bloc scripts
hooks/scriptsAfter.html ...Ă  la fin du bloc scripts


Ces fichiers sont inclus via la commande Jinja {% include "..." ignore missing %}, ce qui permet d'y utiliser également des fonctionnalités Jinja, ou encore du css ou du js en y écrivant les balises appropriées.

C'est notamment la façon idoine de réintroduire des balises <scripts> ou <style> chargeant des fichiers issues de CDNs dans le fichier main.html, si leur insertion via mkdocs.yml:extra_javascript a échoué (1).

  1. Les scripts introduits via extra_javascript sont ajoutés en bas de la page html, juste avant le contenu du footer. Une erreur peut alors être levée si du code JS essaie d'utiliser ce contenu plus haut dans la page, alors que le cdn n'est pas encore chargé (sauf astuces assez moches... :shhhh: ). Dans ce genre de cas, insérer la balise depuis un des crochets libs devrait permettre de résoudre le problème.


Personnaliser le JS de PMT

Il est possible de modifier relativement facilement les classes utilisées dans la couche JS pour y ajouter des nouveaux comportements ou les modifier.

La procédure est la suivante :

  1. Créer un fichier hooks/scriptsBefore.html dans le custom_dir.
  2. Ajouter dans ce fichier une balise <script> qui contiendra le code de la classe personnalisée.
  3. Avant de déclarer la classe personnalisée, vérifier que la classe d'origine est disponible dans l'objet CONFIG.CLASSES_POOL (voir le fichier pyodide_mkdocs_theme/templates/js-libs/0-config.js).
    Le thème ne charge les scripts de définition des classes que si elles sont utilisées dans la page en cours, alors que les fichiers crochets seront lancés sur toutes les pages. D'où la nécessité de vérifier l'existence de la classe avant de déclarer la customisation.
  4. Si la classe à modifier existe, créer la classe personnalisée en étendant la classe d'origine, et y ajouter/modifier tous les comportements désirés.
  5. Une fois la classe déclarée, remplacer la classe d'origine dans CONFIG.CLASSES_POOL par la classe personnalisée.


Exemple

Contenu du fichier hooks/scriptsBefore.html :

<script>
  if(CONFIG.CLASSES_POOL.Ide){
    class YupIde extends CONFIG.CLASSES_POOL.Ide {
      build(){
        super.build()
        this.terminalEcho('Yup!')
      }
    }
    CONFIG.CLASSES_POOL.Ide = YupIde
  }
</script>


Ce qui conduit tous les IDEs Ă  afficher Yup! lors de leur cration :