2 points par GN⁺ 12 시간 전 | 2 commentaires | Partager sur WhatsApp
  • Sur macOS, construire une UI de chat Markdown uniquement avec SwiftUI offre des performances de base correctes, mais il est difficile de prendre en charge la sélection de l’intégralité d’un document
  • En migrant vers NSTextView et TextKit 2, on perd le travail de tests et d’optimisation réalisé avec SwiftUI, et des pics de CPU apparaissent avec les entrées en streaming
  • Une réimplémentation avec NSCollectionView provoque des clignotements des cellules, et même TextKit 2 pur, bien que correct côté performances, s’intègre mal avec le streaming
  • WebKit convient globalement bien pour le rendu Markdown, les performances, la typographie et le niveau de contrôle, et avec Electron, le travail sur le texte fonctionne aussi nativement
  • Pour les longues conversations et le texte enrichi, SwiftUI et les SDK natifs d’Apple deviennent une contrainte, tandis qu’une approche web prend l’avantage grâce à son modèle de texte et de rendu

Les limites d’une UI de chat Markdown native sur macOS

  • En construisant un chat simple prenant en charge Markdown uniquement avec SwiftUI, on peut obtenir des performances de base acceptables, mais il est impossible de sélectionner l’intégralité d’un document Markdown composé de primitives SwiftUI
  • En passant à NSTextView, on gagne la prise en charge de TextKit 2, mais on perd l’essentiel des tests et du travail d’optimisation effectués dans SwiftUI, et l’intégration avec SwiftUI devient médiocre
  • Lorsque les réponses du modèle sont injectées en streaming dans NSTextView, cela provoque des pics de CPU
  • Même une nouvelle implémentation avec NSCollectionView entraîne un clignotement permanent des cellules, un comportement difficile à éviter en raison de la conception même
  • Un prototype en TextKit 2 pur offre des performances correctes, mais le streaming reste mauvais et l’ensemble s’accorde mal avec les composants modernes
  • Même en supprimant complètement SwiftUI pour n’utiliser qu’AppKit, il faut toujours gérer manuellement des fragments de texte qui s’allongent, et la sélection de texte ne devient possible qu’au prix de nombreuses régressions
  • Pour atteindre un niveau comparable au comportement standard de macOS, il faut aussi réimplémenter des fonctions que les utilisateurs considèrent comme acquises, comme le menu contextuel, la consultation du dictionnaire, la sélection, l’accessibilité et les interactions avec le texte

Là où WebKit et Electron s’adaptent mieux

  • En utilisant WebKit pour rendre du Markdown, il y a quelques points d’attention, mais dans l’ensemble cela fonctionne bien, avec de bonnes performances, une bonne typographie et un niveau de contrôle suffisant
  • En créant un projet Electron simple, l’édition de texte, le rendu Markdown et une bonne typographie fonctionnent d’emblée, avec des performances qui se révèlent aussi difficiles à obtenir qu’avec une implémentation TextKit 2 pure
  • Electron offre aussi une intégration macOS, permet de rendre un joli diff Git avec quelques lignes de code, et il existe des exemples comme diffs.com
  • Même après avoir examiné SwiftUI, AppKit, TextKit et WebKit, il reste difficile de satisfaire correctement une exigence pourtant simple : « un chat prenant en charge Markdown et permettant de sélectionner un message en entier »
  • Cela éclaire davantage pourquoi de nouvelles applications, où le chat, le texte enrichi long format et une typographie souple sont importants, choisissent une base web
  • SwiftUI convient bien aux écrans simples avec peu de défilement, et Swift reste utile pour les parties où les performances sont cruciales
  • Electron ou React Native peuvent obtenir de bonnes performances grâce à l’interopérabilité native tout en conservant un meilleur modèle de texte et de rendu
  • Pour le rendu de texte enrichi destiné à des conversations longues, SwiftUI et les SDK natifs d’Apple cessent d’être un avantage et deviennent une contrainte
  • Discussion associée : Hacker News, Lobsters

2 commentaires

 
Réactions sur Hacker News
  • J’ai récemment lancé un éditeur de texte pour iOS utilisant TextKit 2, avec de très bonnes performances même sur des fichiers de 5 000 lignes
    Testé avec Moby Dick du Project Gutenberg, développé entre août 2025 et avril 2026, et le travail continue
    À chaque frappe, le restylage se fait en moins de 8 ms, et même une rafale rapide de 20 frappes est traitée en 150 ms, restylage complet après chaque frappe inclus, sans debouncing ni rendu différé
    Les tags et la recherche booléenne se terminent en moins de 20 ms, et le rendu de la seule zone visible est 25 fois plus rapide que le stylage de tout le document, avec prise en charge d’un rafraîchissement à 120 Hz
    La taille du binaire de l’app était de 722 KB en 1.0, et la 1.1, avec plus de fonctionnalités, semble faire environ 950 KB
    Si c’est possible à ce niveau sur iOS, cela devrait être environ 10 fois plus facile sur macOS
    https://www.gingerbeardman.com/apps/papertrail/

    • Si la fonctionnalité centrale d’un produit payant est l’éditeur, je peux le comprendre. Mais quand le rendu Markdown n’est pas la fonction principale de l’app et ressemble plutôt à une commodité attendue par les utilisateurs en 2026, devoir partir sur une implémentation bas niveau sur mesure avec l’idée que cela prendra 8 mois et nécessitera encore du développement, c’est étrange
      Ce n’est pas que ce soit « impossible », mais ça aide à comprendre pourquoi les gens choisissent les technologies web plutôt que le natif pour ce genre de chose. Ils veulent construire un produit, pas se battre contre les limites du système
    • En tant que personne qui a créé en 2012 une fiction interactive avec NSString et des attributs, un roguelike avec une API de glyphes bas niveau abandonnée, puis deux apps de chat avec support Markdown en SwiftUI et un idle game bourré d’astuces iOS, je finis par résumer cette réaction à une simple question de maîtrise
    • 5 000 lignes, c’est vraiment petit, donc cette explication est déroutante. Même le Moby Dick censé servir de test semble dépasser 22 000 lignes, et le Bloc-notes par défaut de Windows ne rame pas sur un fichier de cette taille
      Pour un visualiseur de texte, il faut au minimum pouvoir gérer des fichiers d’un ordre de grandeur supérieur. Les fichiers JSON de plusieurs centaines de milliers de lignes sont courants, et les CSV comme les logs sont encore plus longs
    • Il est très possible que l’implémentation Mac de SwiftUI soit mauvaise. Pour une app comme ça, SwiftUI-on-Catalyst serait peut-être meilleur, mais il y aura probablement d’autres problèmes
    • L’idée que si ça marche sur iOS alors ce serait 10 fois plus facile sur macOS me paraît hautement douteuse. J’aurais plutôt tendance à penser l’inverse, et j’aimerais entendre l’avis de quelqu’un qui connaît vraiment le sujet
  • En général, on utilisait des API natives plutôt qu’une WebView pour des raisons de performances, mais ce n’est plus forcément vrai aujourd’hui
    Les moteurs de rendu des navigateurs sont très mûrs, largement accélérés par le GPU, et ont subi plus de dix ans de stress tests avec des apps web énormes
    À l’inverse, SwiftUI ne donne pas vraiment une impression de rapidité. Même Réglages système, qu’Apple a reconstruit en version moderne en simplifiant l’UI autour de rangées de cases à cocher, peut parfois saccader davantage qu’une page web chargée depuis us-east-1

    • Le problème ici n’est pas les apps natives en général, mais SwiftUI
      J’ai construit des apps natives en Qt C++ et QML, et j’ai montré qu’elles étaient bien plus rapides et consommaient bien moins de RAM que des apps web comparables
      Donc en règle générale, une app web est plus lente et plus gourmande en ressources qu’une app native bien conçue
      [1] https://notes.alinpanaitiu.com/SwiftUI%20is%20convenient,%20...
      [2] https://x.com/daniel_nguyenx/status/1734495508746702936
      [3] https://rubymamistvalove.com/block-editor#8-performance
    • Cela dit, il reste un écart important entre une app native et l’app navigateur la plus légère, et c’est encore plus visible sur du matériel basse consommation
      J’étais développeur web à l’origine, mais depuis 6 à 12 mois je construis des apps natives multiplateformes, et même sur des tâches simples l’écart de performances est assez net
    • Les navigateurs sont désastreux sur du vieux matériel. Les vieux Chromebook sont courants et ont des specs correctes pour des usages légers ou ciblés, mais le navigateur y tourne vraiment lentement
    • Pour une app web simple, passe encore, mais sur des applications complexes, le ralentissement devient perceptible. Pas besoin d’aller jusqu’à un monstre comme Jira : même une app bien optimisée comme VS Code a un plafond de performances inférieur à celui d’une app native
    • Une WebView, au fond, c’est aussi du « natif »
      Il est étrange de rejeter des milliers d’années-homme d’optimisation et des millions d’années-homme de validation sur le terrain pour réinventer un moteur de rendu de texte moins bon
  • Sur macOS, WebKit est un framework natif de l’OS. Utiliser WebKit pour rendre du Markdown paraît tout à fait approprié
    Évidemment, rendre absolument tout avec WebKit aurait autant de sens que tout rendre avec PDFKit. Mais pour une vue Markdown, WebKit est un choix logique, sans pour autant basculer dans une app web Chromium qui remplace tout

    • Si un moteur HTML rend mieux que les bibliothèques UI natives ce qui est potentiellement le plus difficile à afficher dans une UI, à savoir le texte enrichi, pourquoi ne pas aussi l’utiliser pour des éléments plus simples comme les boutons ou les champs texte ?
      Et OS X a longtemps rendu son UI avec DisplayPDF/Quartz
    • Il faudrait expliquer pourquoi rendre du Markdown avec WebKit est approprié
    • L’auteur original semble considérer que « natif » signifie n’utiliser que des primitives Swift/ObjC
      WebKit existe aussi sur d’autres plateformes, donc ce serait de la triche ? Dans ce cas, on pourrait tout aussi bien utiliser Java
    • Je ne vois pas pourquoi on devrait s’attendre à devoir utiliser WebKit pour rendre du texte enrichi
      Si rendre du texte avec un moteur HTML/CSS/JS est « tout à fait approprié », alors quel domaine ne l’est pas ? Pourquoi ne pas tout rendre ainsi ?
      Je ne comprends pas la logique qui mène de « le rendu de texte, c’est approprié » à « mais tout rendre ainsi, ce serait absurde »
    • En pratique, la solution en cours consiste justement à rendre le Markdown final et le streaming avec WebKit
      Je suis d’accord sur le fait que WebKit est un framework natif de macOS et qu’en ce sens c’est bien du « natif »
      Mais cela renforce aussi l’idée plus large selon laquelle, dès qu’il s’agit de bien gérer du texte enrichi, du Markdown, la sélection, la typographie ou du contenu long format mis en forme, les technologies web deviennent rapidement la seule option réellement pratique
      Ce n’est pas que WebKit soit un mauvais choix pour une vue Markdown, au contraire, c’est probablement le plus rationnel. Le problème, c’est qu’ici la solution « native » est en fait une solution de rendu web
      Chaque WKWebView embarque son propre moteur WebKit, avec son coût en performances et en mémoire, donc on ne peut pas en parsemer partout en les traitant comme des composants natifs macOS gratuits
      Ce qui est frustrant, c’est que SwiftUI / AppKit / TextKit n’offrent pas, pour ce type d’UI, une voie propre, moderne et composable meilleure que « utilise simplement WebKit »
  • Le fait de ne même pas pouvoir faire quelque chose d’aussi basique que « permettre de sélectionner l’intégralité d’un message dans un chat avec Markdown » semble absurde
    SwiftUI permet d’utiliser des renderers Markdown mûrs. Il suffit de regarder https://github.com/gonzalezreal/swift-markdown-ui et son remplaçant de nouvelle génération https://github.com/gonzalezreal/textual
    Je les ai utilisés moi-même et ça marchait sans problème. Même moi, idiot qui n’aime ni Swift ni SwiftUI et préfère Objective-C, j’y suis arrivé sans aide de LLM

    • J’ai testé Textual plus tôt aujourd’hui, et les résultats n’étaient pas terribles
      Le scroll d’un Markdown statique terminé n’a pas passé le nouveau test de focalisation, avec un p95 à 18,86 ms au-dessus du budget de 16,7 ms, et un maximum à 232,49 ms
      Le scénario de longues mises à jour Markdown/code en temps réel a aussi échoué, avec p95 à 59,33 ms contre 16,7 ms, et un max à 75,94 ms. C’est un cas de stress distinct mais lié, qui consiste à manipuler de grandes surfaces de texte enrichi pendant les mises à jour
      L’extension d’un long historique passe techniquement, mais difficile de parler d’images fluides : 120 tours p95 à 21,35 ms, 500 tours à 23,11 ms, 1000 tours à 36,77 ms
      Ce n’est pas mauvais, mais c’est légèrement plus lent que ma solution, avec un écart de performances similaire qui semble surtout lié à SwiftUI plutôt qu’à l’implémentation de Textual
    • Est-ce que ça peut gérer du streaming de nouveau texte sans clignotement ?
    • Pour un document Markdown rendu une seule fois, ou si le document est simple et court, probablement
      J’ai déjà utilisé swift-markdown-ui dans une app, mais côté performances ce n’était même pas proche de wkwebview. Si on fait du streaming sur de gros documents avec des éléments compliqués comme de grands tableaux, des blocs de code ou des citations imbriquées, on peut même finir avec le beachball, ce qui ne m’est jamais arrivé avec wkwebview
    • La première bibliothèque semble être utilisée par l’app iOS de Claude, et elle paraît permettre la sélection de texte ainsi que le streaming, avec des performances correctes
    • En tant qu’utilisateur, on voit bien que de vieilles apps non basées sur HTML ne respectent pas les « règles ». Du texte qui devrait être sélectionnable ne l’est pas, ou bien control/option C ne copie pas
      Puis on se rend compte que le navigateur et ses technologies sous-jacentes ont introduit un nouveau paradigme pour l’UI, que les frameworks UI natifs n’ont pas réussi à suivre
      Même en préférant les apps natives aux apps web, c’est l’impression que cela donne
  • Montrez le code, sinon il faut arrêter là. Il existe déjà énormément d’apps Mac/iOS natives qui gèrent très bien le rendu Markdown et le texte en streaming
    J’aimerais juste savoir quelle excuse est avancée ici

    • Il dit vouloir « sélectionner tout un document Markdown construit avec des primitives SwiftUI », mais qui veut ça ? Comme décision produit, cela revient pratiquement à vouloir fabriquer un éditeur de documents, un domaine difficile depuis des décennies, et qui semble sortir du cadre d’une UI de chat LLM
      La plupart des gens ont fini par se limiter à la sélection à l’intérieur de chaque bloc continu, avec un bouton de copie pour l’ensemble du message
    • Si c’est possible sans WebView, il suffit de partager le code
  • Ce qui est amusant, c’est qu’Apple faisait déjà ce genre de chose autrefois
    Les anciennes versions de macOS / AppKit utilisaient WebKit pour rendre le texte enrichi dans les NSTextField natifs. Le texte est un problème difficile
    Et puis la WebView native est très rapide et légère, donc l’utiliser comme moteur de mise en page du texte n’a rien d’étrange. On peut même avoir d’excellentes performances avec une WebView distincte pour chaque ligne d’un tableau
    iMessage sur Mac utilisait aussi une WebView, tout comme Adium. Si on rend du texte riche / balisé, HTML est un outil totalement approprié

    • Il y a ici une confusion entre iOS et Mac OS
      Le Mac n’a jamais utilisé WebKit pour le rendu des NSTextField. Lors de la création initiale d’iOS, WebKit servait largement de moteur de rendu de texte, y compris pour les contrôles UIKit, et cela était présenté comme une « sweet solution »
      Mais cela s’est révélé trop lourd et trop contraignant, ce qui a conduit à l’adoption d’une approche de rendu de texte à la Core Text/AppKit
    • Le raisonnement du texte d’origine est un peu étrange
      Il découvre que le rendu de texte natif complexe est difficile, tente une approche bas niveau, puis se plaint de devoir réimplémenter les interactions natives
      Il essaie WebKit, constate que ça marche très bien, puis abandonne pour revenir à une situation où il faut à nouveau réimplémenter les interactions natives
      Personnellement, je me serais arrêté au moment où WebKit fonctionnait bien
  • Je me souviens qu’en 2015, quand j’étais ingénieur junior, on m’avait demandé de rendre des liens cliquables dans des paragraphes d’une app iOS
    Swift venait juste de sortir, donc c’était une stack entièrement ObjC/UIKit, et c’était un vrai cauchemar. J’avais fini par le faire marcher à peu près
    Comme je n’ai presque plus touché à iOS depuis 2016, j’imaginais naturellement que le nouveau SwiftUI intégrerait ce genre de chose de base, donc découvrir que non, c’est assez dingue

    • Il y a littéralement un composant Link
      https://developer.apple.com/documentation/swiftui/link
      À ce stade, je ne vois pas comment on pourrait faire plus simple
    • Qt permettait déjà de faire ça assez facilement il y a 10 ans
    • Il n’y avait pas NSLinkAttributeName ?
    • Je pensais que le texte attribué gérait ça correctement depuis longtemps, non ?
    • La demande même de mettre des liens cliquables à l’intérieur de paragraphes était déjà une mauvaise idée
  • Il est normal qu’en sortant des écrans simples, le natif paraisse encore aussi immature
    Si les gens n’y investissent pas suffisamment d’efforts, on ne peut pas s’attendre à ce que ça mûrisse
    Comme plus d’efforts vont vers les technologies web, les gens s’y retrouvent enfermés. Ils regardent le natif, disent qu’il n’est « pas assez avancé », puis retournent faire encore plus de développement web, et le cycle se répète
    Dans le navigateur, comme « ça marche tout simplement », presque plus personne n’a envie de faire l’effort d’améliorer le natif

    • Même dans ce cas, les toolkits UI natifs restent des produits commerciaux, non ? Ce n’est pas aux gens de se convaincre eux-mêmes, c’est à eux de convaincre les gens
      Si le web est bien plus mûr, c’est notamment parce que les grands fabricants d’OS commerciaux n’ont pas voulu suivre leur époque. Les toolkits UI sous Windows sont vraiment affreux
    • D’accord. Au fond, cela revient à se plaindre que très peu d’efforts ont encore été investis pour manipuler rapidement du Markdown en Swift, tout en ne cherchant pas soi-même à contribuer dans cette direction
  • J’ai eu presque exactement la même expérience dans mon app de chat IA. Rien ne fonctionne correctement
    Le rendu Markdown est lent et saccadé, le streaming est lent et saccadé, et tout finit par bloquer l’UI
    J’ai essayé au moins cinq composants d’édition de texte populaires sur GitHub pour UIKit et SwiftUI, et tous étaient cassés d’une façon ou d’une autre, buggés et lents. C’est absurde

  • C’est un problème difficile. J’ai écrit longuement sur la façon dont je l’ai abordé en construisant un éditeur par blocs de zéro avec Qt C++ et QML
    J’ai rencontré des problèmes similaires : sélection à travers des blocs discontinus, affichage du Markdown source sous le curseur, tailles de delegates différentes
    En m’appuyant sur ce que j’ai appris alors, je construis maintenant un client LLM natif avec parseur Markdown en streaming
    [1] https://rubymamistvalove.com/block-editor
    [2] https://www.get-vox.com

 
Commentaires sur Lobste.rs
  • Je suis d’accord pour dire qu’une mise en page de texte complexe se prête bien à une webview, mais je ne suis pas convaincu qu’il faille forcément aller jusqu’à Electron pour des raisons de commodité
    Electron n’est au fond qu’un wrapper autour d’une WebView, mais comme il embarque tout le moteur Chromium, la contrepartie de cette commodité est une taille d’application beaucoup trop importante
    En 2001~2002, j’avais implémenté la fameuse mise en page en bulles de chat d’iChat avec NSTextView et toutes sortes de bricolages ; même avec l’aide de Hideki Itamura, qui concevait le système de texte d’AppKit, ça avait été assez pénible. Aujourd’hui, c’est devenu assez simple avec HTML+CSS
    • Quand il faut prendre en charge plusieurs systèmes, la simplicité finit par peser lourd
      J’ai travaillé sur une application qui utilisait Tauri, et une fois passée à Electron avec Chromium, elle fonctionnait bien mieux. Le fait de viser une plage très large, de win7 à win11, comptait aussi beaucoup
      Les outils de la famille Tauri paraissent séduisants parce qu’ils utilisent la webview du système, mais sur Windows cela veut dire Chrome ou Edge, sur macOS Safari, et ailleurs c’est un peu la loterie. Au final, la prévisibilité et la maturité l’ont emporté, et pour une raison inconnue les performances étaient aussi meilleures
    • À mon avis, c’est une préférence personnelle. Que ce soit la taille de l’application ou l’usage de la RAM, certains sont prêts à payer le prix de la commodité, et les millions d’utilisateurs d’applications comme Visual Studio Code semblent l’accepter eux aussi
      Au bout du compte, on veut surtout de meilleurs logiciels plutôt que de satisfaire les graphiques de htop
      L’idée centrale du billet n’est pas de dire « tout le monde devrait choisir Electron », mais que je comprends désormais pourquoi même des entreprises qui ont des ressources et de l’argent continuent de choisir Electron ou les technologies web. Au moins de mon point de vue, cela permet de faire les bons compromis aux bons endroits tout en offrant une bonne expérience utilisateur
      Beaucoup défendent TextKit 2 d’Apple ou d’autres frameworks de texte natifs, mais il n’existe pratiquement aucun éditeur de texte populaire, performant et construit uniquement avec les SDK Apple. Xcode s’en sort plutôt bien et est probablement le seul cas proche d’un usage réel à grande échelle. Zed, Sublime Text, Visual Studio Code et les IDE JetBrains utilisent tous leur propre solution de rendu de texte, et ce n’est pas pour rien
  • L’un des problèmes, c’est que les gens qui fabriquent les logiciels développent sur des ordinateurs et des téléphones parmi les 0,1 % meilleurs au monde
    Du coup, même quelque chose conçu de la pire manière peut leur sembler acceptable dans leur propre environnement
    Pendant ce temps, tous les autres, avec des appareils bien moins puissants, continuent de subir des logiciels lourds et lents
  • Je me demande où en sont aujourd’hui les écosystèmes comme GTK/Qt. Ça fait longtemps que je n’ai pas fait de GUI
    • Si j’ai bien compris, cela devrait aussi pouvoir être implémenté en Qt sans webview
      Cela dit, l’auteur n’a pas l’air d’avoir exploré cette piste jusqu’à ce niveau
  • Je serais curieux de voir une comparaison entre Electron et Flutter
    Vu de l’extérieur, Flutter ressemble à une réponse à la question : « et si on gardait seulement Skia, le moteur de rendu de Chromium, pour en faire un outil d’applications GUI ? » Cela devrait être un outil multiplateforme plus léger qu’Electron tout en offrant des fonctionnalités similaires
  • L’auteur de https://github.com/stevengharris/MarkupEditor propose cet article comme une solution plutôt bonne parmi celles qui sont actuellement possibles : https://blog.krzyzanowskim.com/2025/08/14/textkit-2-the-promised-land/
    • Sauf que MarkupEditor n’utilise pas TextKit2, mais une WebView. Le README indique aussi que MarkupEditor interagit avec un document HTML utilisant un unique DIV contentEditable, et qu’il emploie une sous-classe de WKWebView
      Ce qui est ironique, c’est que le moteur de texte natif d’Apple utilise un modèle de données bien meilleur que l’arbre DOM HTML, à savoir une chaîne de caractères et des attributs de style encodés par plages d’exécution
      Éditer du texte dans le DOM est presque un cauchemar, car la sélection traverse souvent plusieurs éléments sur plusieurs niveaux, ce qui oblige à faire des découpages et des fusions très complexes. Quand j’ai mis la main sur les premiers builds de Safari avec prise en charge de contenteditable, j’ai envoyé énormément de rapports de bugs, et encore aujourd’hui beaucoup d’éditeurs de texte enrichi sur le web se cassent lorsqu’on coupe ou colle des éléments de liste
      Au final, on dirait qu’il s’est produit quelque chose d’analogue au débat CISC contre RISC des années 90~2000. Une architecture « inférieure » a reçu davantage de ressources et a fini par produire une implémentation supérieure