FonctionnalitésTarifsDocumentationBlog
Obtenir mote
← Blog
Technique·28 juin 2026·7 min de lecture

Pourquoi nous avons placé des interfaces propres à chaque plateforme sur un cœur Rust unique

Pour proposer le même éditeur sur trois systèmes d’exploitation, il existe une voie qui consiste à ne concevoir l’interface qu’une seule fois. Nous l’avons empruntée, puis avons fait demi-tour pour superposer à un cœur unique un ensemble d’enveloppes légères propres à chaque plateforme.

m
mote
Les enveloppes sont en haut, le cœur au milieu, et le rendu du texte ainsi que les seuils de validation en bas. L’interprétation du Markdown, les opérations d’édition et l’écriture des fichiers se trouvent toutes dans la boîte centrale.
Les enveloppes sont en haut, le cœur au milieu, et le rendu du texte ainsi que les seuils de validation en bas. L’interprétation du Markdown, les opérations d’édition et l’écriture des fichiers se trouvent toutes dans la boîte centrale.

Pour proposer le même éditeur sous Linux, macOS et Windows, il faut créer l’interface trois fois.

Au début, nous voulions éviter ce coût. Nous avons donc dessiné le même écran sur les trois systèmes d’exploitation à l’aide d’un seul toolkit multiplateforme. Cette décision nous a permis de franchir notre premier seuil de performance ainsi que notre premier jalon concernant la saisie du coréen.

Puis nos priorités ont changé.

Les performances sont plus importantes que le design, et le design est plus important que le coût. Le temps et le coût ne sont plus considérés comme des contraintes.

En réexaminant la situation selon ces critères, nous avons obtenu une réponse différente.

Nous avons renoncé à une interface unique

Un toolkit multiplateforme présente des avantages évidents. Une fonctionnalité créée une seule fois peut être utilisée sur plusieurs systèmes d’exploitation.

Mais cela avait aussi un coût. Il consommait 40 à 60 Mo de mémoire supplémentaires et ralentissait le lancement de 150 à 200 ms. Le rendu des caractères était moins satisfaisant que celui du moteur de texte natif du système d’exploitation, et nous ne pouvions pas non plus exploiter pleinement les effets visuels propres au système.

C’était un choix raisonnable lorsque le coût de développement comptait. Mais une fois le coût retiré des contraintes, nous n’avions plus de raison de l’accepter.

Nous avons donc décidé de créer en Rust le cœur chargé du traitement des documents et de développer séparément, pour chaque système d’exploitation, l’enveloppe responsable de l’affichage.

Ce n’est pas parce que la technologie précédente était mauvaise. Lorsque les priorités changent, les mêmes options peuvent conduire à une réponse différente. Nous avons choisi de payer le prix d’une triple implémentation des fonctionnalités pour retrouver les performances, la qualité du texte et une expérience fidèle à chaque système.

Nous n’avons toutefois pas tout abandonné.

Nous avons conservé tel quel notre ensemble de règles de mesure. Placer le pointeur en dehors de la fenêtre, identifier précisément la fenêtre par son nom et compter les images de manière synchrone. Nous avons également repris nos principes de conception pour la saisie du coréen. Nous ne montrons au système d’exploitation que la ligne sur laquelle se trouve le curseur, et nous n’appliquons aucune mise en forme aux caractères en cours de composition. Lorsque les états divergent, nous ne faisons pas de supposition : nous les resynchronisons.

Même si le code est abandonné, les principes éprouvés subsistent.

Nous avons réduit le rôle des enveloppes

Si les enveloppes propres aux différentes plateformes commencent à assumer trop de responsabilités, les trois produits divergent rapidement. Nous avons donc défini clairement ce qu’une enveloppe doit faire et ce qu’elle ne doit pas faire.

L’enveloppe crée la fenêtre et dessine les caractères. Elle transmet au cœur les entrées du clavier et du pointeur, et transforme la saisie du coréen en commandes d’édition. Elle applique également les effets visuels du système d’exploitation.

En revanche, elle n’interprète pas le Markdown. En dehors des retours à la ligne, elle ne détermine pas non plus la mise en page, et elle ne sauvegarde pas les fichiers ni ne prend de décisions concernant les licences.

Nous ne nous sommes pas contentés de consigner ce principe dans la documentation. Si du code d’interprétation du Markdown est introduit dans une enveloppe, la compilation est interrompue par un contrôle automatique.

Si une seule enveloppe commence à traiter la syntaxe, les résultats risquent de différer légèrement selon le système d’exploitation. Ce genre de problème est également difficile à trouver. Si un tableau apparaît différemment dans une seule enveloppe, la cause réelle peut très bien ne pas se trouver dans le code qui dessine le tableau.

Nous avons conçu le cœur pour qu’il soit le seul à durer

Le cœur ne dépend d’aucune technologie d’interface. Il peut être testé seul dans un terminal, même sans fenêtre.

Le cœur prend en charge l’édition des documents, l’interprétation du Markdown, l’annulation, la recherche, le plan, la sauvegarde des fichiers, l’exportation et la validation des licences.

Tests du cœur exécutés dans un terminal sans interface. Les 194 tests unitaires ainsi qu’un test distinct des blocs incrémentaux ont réussi.
Tests du cœur exécutés dans un terminal sans interface. Les 194 tests unitaires ainsi qu’un test distinct des blocs incrémentaux ont réussi.

Lorsque l’utilisateur appuie sur une touche, l’enveloppe transmet cette entrée au cœur. Le cœur modifie le document et ne réinterprète que la zone entourant la partie modifiée. Après avoir calculé uniquement le contenu visible à l’écran, il renvoie à l’enveloppe les résultats qui ont changé. L’enveloppe ne redessine alors que les lignes concernées et achève une image.

Si le document entier est relu au cours de ce processus, il ne s’agit pas d’un simple problème de performances. Cela signifie que nous avons dépassé le budget fixé au départ.

L’exactitude n’est elle aussi vérifiée qu’une seule fois, dans le cœur. Les 652 exemples officiels de CommonMark doivent tous réussir, et la compilation échoue si le nombre de réussites diminue. Nous effectuons également des tests de fuzzing qui injectent continuellement des entrées inattendues.

Nous vérifions aussi que le fichier sauvegardé est identique à l’original au niveau des octets. Si le taux de conservation après aller-retour est inférieur à 1,0, cela ne signifie pas que le programme est lent, mais que nous avons perdu des données du fichier.

Même lorsque l’enveloppe change, le cœur reste inchangé. Sous Linux et Windows, nous connectons directement le cœur, tandis que sous macOS, nous utilisons du code de liaison en Swift. L’enveloppe macOS est réalisée avec AppKit et TextKit 2, et celle de Windows avec DirectWrite.

Notre objectif est de conserver des documents identiques tout en proposant une prise en main propre à chaque système d’exploitation.

Les documents sont identiques, mais l’expérience diffère

Une fois les frontières définies, ce qui devait être uniformisé est également devenu plus clair.

Ce qui doit être identique, ce sont les documents. Quel que soit le système d’exploitation depuis lequel un fichier est sauvegardé, son contenu doit être identique au niveau des octets. L’alignement des tableaux, les fins de ligne et même le dernier retour à la ligne ne doivent pas changer.

L’exactitude du Markdown est déjà vérifiée dans le cœur. Il n’est pas nécessaire de la démontrer à nouveau chaque fois que nous créons une nouvelle enveloppe. Sur une nouvelle plateforme, ce qu’il faut vérifier, ce sont l’expérience d’utilisation et les performances.

À l’inverse, nous ne cherchons pas à uniformiser l’expérience directement ressentie par l’utilisateur.

Les caractères sont dessinés à l’aide du moteur de texte de chaque système d’exploitation. Nous utilisons également directement le système de saisie du coréen propre au système d’exploitation. Pour les effets de transparence, nous utilisons les fonctions du système sous macOS et Windows, tandis que sous Linux, où les mêmes fonctions n’existent pas, nous ne dessinons nous-mêmes que les parties nécessaires.

Nous ne cherchons pas non plus à imposer le même emplacement aux boutons de fenêtre, la même forme aux menus ni les mêmes touches modificatrices aux raccourcis. Notre objectif n’est pas d’afficher les mêmes pixels sur des systèmes d’exploitation différents. Il est plus important que l’application paraisse naturelle sur chacun d’eux.

Nous avons également défini les conditions de lancement séparément pour chaque système d’exploitation. Une version ne peut être publiée qu’après avoir satisfait aux critères de performance et passé des tests réels avec la méthode de saisie coréenne par défaut.

Une ligne correspondant à une plateforme n’est ajoutée au tableau des benchmarks qu’après la production effective de sa version. Si nous ajoutions à l’avance une ligne qui n’a pas été mesurée, il deviendrait difficile de faire confiance au reste du tableau.

Ce choix a un coût bien réel

Nous devons créer trois fois les mêmes fonctionnalités. Nous devons aussi implémenter trois fois la saisie du coréen et maintenir trois environnements de compilation.

Nous ne pouvons ni compiler ni déboguer les versions Windows et macOS sur la machine que nous utilisons actuellement. Du matériel et des environnements d’automatisation distincts sont nécessaires. C’est pourquoi nous ne fixons aucune date pour ces deux plateformes.

Nous avons également défini les conditions dans lesquelles cette décision devra être réexaminée. Le fait d’exclure le coût des contraintes constitue le postulat de cette décision. Si nous ne pouvons pas obtenir le matériel nécessaire, seul le calendrier de la plateforme concernée sera retardé. Le lancement sous Linux se poursuivra comme prévu.

Une décision doit être accompagnée non seulement de ses raisons, mais aussi des conditions dans lesquelles elle devra être reconsidérée. Ainsi, si la situation change, nous n’aurons pas à reprendre le débat depuis le début.

Ce qui doit durer, c’est le cœur. Les enveloppes doivent pouvoir être abandonnées à tout moment.

Nous n’avons confié aux plateformes que ce qui peut être abandonné.

← Plus anciensMême lorsque l’on change de mode, le document reste uniquePlus récents →Pourquoi nous avons confié le rendu des caractères à la plateforme au lieu de les dessiner nous-mêmes
Suivez la suite par RSS.
Ne garder que l’écriture.
Français
© 2026 mote