Cette page décrit chaque partie de Kaivalya Editor telle qu’elle est. Pour le compiler une première fois, voir la Mise en route ; pour une tâche précise — ajouter un réglage, une commande, une mesure —, voir les Guides pratiques ; pour comprendre pourquoi il est construit ainsi, voir Comprendre l’architecture.

Code écrit par une IA. L’essentiel du code source a été écrit par un assistant IA (Claude), sous la direction de l’auteur humain, qui a relu, validé et décidé chaque modification. C’est précisé ici par transparence.

1. L’espace de travail

Un espace de travail Cargo de treize crates, en Rust édition 2024, sous licence AGPL-3.0-or-later. Seule kve-ui importe le framework graphique ; chaque crate marquée sans interface se teste sans fenêtre.

Crate Sans interface Ce dont elle s’occupe
kve-core oui Le tampon de texte (une rope), les positions en graphèmes, les modifications et l’annulation ; la lecture des couleurs ; le lancement des formateurs externes
kve-config oui Les réglages et leur fichier auto-documenté, le registre des commandes et ses raccourcis, le catalogue de la palette /, les sessions, les emplacements récents, l’historique local, les deux catalogues de traduction
kve-markdown oui Le markdown : le HTML des aperçus et des exports, le plan, la table des matières, les tableaux, les listes, les liens et les rétroliens, la recherche des TODO, le todo.md du tableau des tâches, le formateur intégré
kve-format oui Les autres formateurs intégrés : JavaScript et TypeScript, JSON, CSS, HTML, XML, Vue, YAML, TOML, PHP
kve-fs oui L’arborescence du projet, les règles d’exclusion, la surveillance des fichiers, la création, le renommage et la suppression de fichiers
kve-search oui L’index des fichiers et sa recherche approximative ; la recherche et le remplacement dans tout un projet
kve-git oui Git via gitoxide : état, différences, indexation, commit, historique, blame ; git lui-même pour la récupération, l’envoi, le rebasage et le changement de branche
kve-canvas oui Le JSON Canvas d’Obsidian : lu, modifié et réécrit champ par champ ; la géométrie du tableau ; son export SVG
kve-ui non La fenêtre : chaque vue, panneau et boîte de dialogue, avec gpui et gpui-component
kve-preview non L’aperçu complet : un second programme, une fenêtre qui dessine des pages HTML entières avec Blitz
kaivalya-editor non L’exécutable : les arguments, puis la fenêtre
kve-bench oui Le banc de mesure et les budgets qu’il fait respecter
kve-drive — Un outil de développement : un pointeur et un clavier pour une fenêtre lancée sous cage

Comment les crates dépendent les unes des autres

Les flèches vont d’une crate vers ce qu’elle utilise. Tout ce qui se trouve sous kve-ui est sans interface ; kve-ui est la seule à savoir qu’une fenêtre existe, et seul l’exécutable en dépend. kve-bench utilise directement les crates sans interface, pour les mesurer sans fenêtre.

flowchart TD bin["kaivalya-editor<br/>l'exécutable"] --> ui["kve-ui<br/>la fenêtre"] bin --> config ui --> config["kve-config<br/>réglages, commandes, traductions"] ui --> core["kve-core<br/>tampon, modifications, annulation"] ui --> md["kve-markdown<br/>markdown, HTML, plan, tableaux"] ui --> fs["kve-fs<br/>arborescence, règles, surveillance"] ui --> search["kve-search<br/>index, recherche dans le projet"] ui --> git["kve-git<br/>git via gitoxide"] ui --> format["kve-format<br/>formateurs intégrés"] ui --> canvas["kve-canvas<br/>JSON Canvas"] ui --> preview["kve-preview<br/>l'aperçu complet"] config --> md search --> fs ui -.->|lance, envoie les pages par un tube| previewbin(["processus kve-preview"]) bench["kve-bench"] -.-> core & md & fs & search & git & format & canvas & config

Les fichiers principaux

Crate Où regarder d’abord
kve-ui window.rs — la fenêtre de l’éditeur : onglets, panneaux, menus, le traitement de chaque commande ; canvasview.rs le tableau blanc, boardview.rs le tableau des tâches, logview.rs, gitviews.rs (historique, graphe, blame), diffview.rs, conflictview.rs, grid.rs la fenêtre de tableau, formula.rs, heavy.rs le côté émetteur de l’aperçu complet, panewindow.rs un panneau dans sa propre fenêtre, theme.rs les jetons de couleur, syntax.rs la coloration, menu.rs les menus
kve-config settings.rs les réglages et leur fichier commenté, command.rs le registre des commandes, palette.rs le catalogue de la palette /, session.rs, places.rs, history.rs l’historique local, characters.rs, rules.rs le fichier du projet ; locales/ les deux catalogues
kve-markdown html.rs le HTML des aperçus et des exports, outline.rs, toc.rs, table.rs, list.rs, board.rs le todo.md du tableau des tâches, mentions.rs les rétroliens, wikilink.rs, transclude.rs les inclusions, format.rs le formateur, spelling.rs, maths.rs, mermaid.rs, stylesheet.rs
kve-core document.rs le tampon, edit.rs, position.rs les graphèmes, colour.rs, external.rs le lancement d’un formateur
kve-fs tree.rs l’arborescence du projet, rules.rs les règles d’exclusion, watch.rs la surveillance, make.rs créer, renommer, mettre à la corbeille
kve-search index.rs et matcher.rs le sélecteur de fichiers, content.rs la recherche et le remplacement dans le projet, glob.rs
kve-canvas lib.rs le modèle, lu et écrit, geometry.rs le repérage et les courbes, edit.rs les modifications, svg.rs l’export
kve-git lib.rs : état, différences, commit, historique, graphe, blame, et les appels à git
kve-preview main.rs la fenêtre du second programme, lib.rs ce qu’on lui envoie

spike/ contient des mesures faites avant une décision, gardées hors de l’espace de travail pour que chacune puisse fixer ses propres versions. crates/kve-ui/src/window.rs est la fenêtre de l’éditeur — de loin le plus gros fichier —, et chaque autre vue a son propre fichier : canvasview.rs (le tableau blanc), boardview.rs (le tableau des tâches), logview.rs, gitviews.rs, grid.rs (la fenêtre de tableau), formula.rs, heavy.rs (le côté émetteur de l’aperçu complet), panewindow.rs (un panneau dans sa propre fenêtre).

2. Chaîne de compilation et dépendances

  • Rust 1.98.0, fixé par rust-toolchain.toml, avec rustfmt et clippy. Le changer est un acte délibéré : relancer les mesures et les consigner.
  • Toutes les dépendances viennent de crates.io, framework graphique compris : gpui est le paquet gpui-pre, gpui-component et sa famille sont en 0.6. Il n’y a aucune dépendance git.
  • Cargo.lock est versionné, et c’est lui qui fixe les versions. Chaque compilation et chaque tâche de CI passe --locked. On ne lance jamais un cargo update sans argument : il résoudrait tout vers les versions les plus récentes et changerait le framework sous une mesure. Le retrait d’une crate de l’espace de travail s’enregistre avec cargo update --workspace --offline, qui ne touche qu’aux entrées de l’espace de travail lui-même.
  • .cargo/config.toml utilise l’éditeur de liens mold via clang quand ils sont installés.

3. Compiler

make Ce que fait la cible
build L’éditeur avec l’aperçu complet, et kve-preview, en mode release
build-light L’éditeur seul, sans l’aperçu complet — quelques secondes au lieu de plusieurs minutes
check Les cinq vérifications de la CI, dans son ordre : fmt, clippy, doc, test, bench
fmt cargo fmt --all --check
clippy cargo clippy --workspace --all-targets --locked -- -D warnings
doc cargo doc sur toutes les crates, éléments privés compris, avertissements traités comme des erreurs
test cargo test --workspace --locked
bench kve-bench en mode release : renvoie 1 si un budget est dépassé
site Les pages de référence générées, puis le site web avec Hugo
walkthrough Les vérifications qui demandent une vraie fenêtre, pilotées sous cage, une capture chacune
deb Un paquet Ubuntu/Debian de la version compilée, target/deb/kaivalya-editor_<version>_<arch>.deb — tools/deb.sh : make install dans un dossier intermédiaire sous /usr, exécutables allégés de leurs symboles, dépendances calculées par dpkg-shlibdeps plus Vulkan et Wayland (ouverts à l’exécution, non liés). Il s’installe sur la version d’Ubuntu où il a été construit et les suivantes
sync Recopie project.toml dans les fichiers qui ne savent pas le lire : le repository de Cargo.toml, le développeur, la page d’accueil et le dépôt du fichier metainfo. Un test de kve-config échoue tant qu’il n’a pas été lancé
install / uninstall Les deux programmes, l’entrée de bureau, les métadonnées AppStream et les icônes, sous PREFIX (~/.local par défaut)

La fonctionnalité Cargo heavy-preview de kaivalya-editor décide si une version compilée contient le moteur de l’aperçu complet ; le réglage heavy_preview décide si une version qui le contient le propose.

4. Les fichiers que l’éditeur lit et écrit

Emplacement Contenu
~/.config/kaivalya-editor/settings.toml Vos réglages, écrits avec chaque option commentée s’il n’en existe pas
~/.config/kaivalya-editor/themes/ Vos thèmes, au format JSON de gpui-component
~/.config/kaivalya-editor/styles/ Vos feuilles de style pour les aperçus et les exports
~/.config/kaivalya-editor/templates/ Vos modèles de notes
~/.local/share/kaivalya-editor/sessions/<empreinte>.toml Ce qui était ouvert dans un projet, identifié par une empreinte FNV-1a de son chemin
~/.local/share/kaivalya-editor/places.toml Les fichiers et dossiers récents et favoris
~/.local/share/kaivalya-editor/history/ L’historique local : une version d’un fichier à chaque enregistrement
<projet>/.kaivalya/settings.toml Les réglages propres à un projet : seulement ce qui diffère des vôtres
<projet>/.kaivalya/project.toml Les règles de dossier d’un projet, comme la feuille de style des notes d’un dossier
<projet>/.kaivalya/templates/ Les modèles propres à un projet
<projet>/todo.md Le tableau des tâches et Mes tâches

Les variables XDG_CONFIG_HOME et XDG_DATA_HOME déplacent les deux premiers dossiers ; la visite guidée automatisée et le script de captures d’écran s’en servent pour travailler sur une configuration jetable. Rien n’est écrit dans un projet sans que vous le demandiez : les sessions et l’historique sont rangés dans votre dossier de données.

5. Les réglages

  • Les valeurs par défaut sont un fichier, resources/settings/defaults.toml, intégré à l’exécutable. Aucune valeur par défaut n’est écrite en Rust : un champ de Settings sans valeur dans ce fichier fait échouer les tests.
  • Settings::parse est la seule porte d’entrée. Un fichier partiel est fusionné par-dessus la table des valeurs par défaut : un fichier de réglages n’a besoin de mentionner que ce qu’il change. Une clé que l’éditeur ne connaît plus est ignorée.
  • Le fichier se documente lui-même. Chaque option y est écrite avec son explication, tirée du catalogue de la langue du lecteur. Un test refuse un réglage qui n’est pas expliqué dans chaque catalogue.
  • Les tests ne lisent jamais defaults.toml. Ils utilisent resources/settings/for-tests.toml via Settings::for_tests() (la fonctionnalité test-support), dont les valeurs ne bougent pas ; un test vérifie que les deux fichiers ont les mêmes clés. Un test peut vérifier une propriété des vraies valeurs par défaut, jamais une valeur.
  • Le .kaivalya/settings.toml d’un projet est fusionné par-dessus les vôtres de la même façon.

6. Commandes, menus et raccourcis

  • Un seul registre, crates/kve-config/src/command.rs : chaque commande a un id (file.save), une portée, une clé de catalogue pour son libellé (command.file.save), un raccourci par défaut et éventuellement un groupe (un sous-menu).
  • crates/kve-ui/src/menu.rs associe chaque identifiant à son action gpui, et bindings() dans window.rs installe les raccourcis. Les menus, la page de référence des raccourcis et les raccourcis eux-mêmes lisent la même table : un raccourci ne peut pas être affiché à un endroit et associé à un autre.
  • Les raccourcis d’édition du texte (Ctrl+Z, Ctrl+C…) appartiennent au composant d’édition, dans son contexte Input ; une vue qui n’est pas du texte — le tableau blanc, la vue journal — gère elle-même ses touches.

7. Les traductions

  • rust-i18n, deux catalogues : crates/kve-config/locales/en-US.yml et fr-FR.yml. Tout texte visible par l’utilisateur passe par eux — t!(), ou kve_config::tr() et les fonctions voisines pour les textes avec paramètres.
  • Un test échoue si les deux catalogues n’ont pas exactement les mêmes clés, ou si l’un d’eux déclare une clé deux fois.
  • La langue est donnée par le réglage locale, ou à défaut par le $LANG du bureau.

8. L’aperçu complet

kve-preview est un programme séparé, que l’éditeur lance à la demande et cherche à côté de lui. L’éditeur lui envoie des pages HTML entières via un tube — le document tel que l’écrit kve-markdown, avec la feuille de style et les formules déjà dessinées — ; l’aperçu ne lit jamais de fichier et ne demande jamais rien à l’éditeur. Son moteur de rendu est Blitz (Stylo, Taffy, Vello), fixé à des versions exactes. Il n’a pas accès au réseau.

9. Le tableau blanc

Un fichier .canvas est lu par kve_canvas::parse dans un Canvas — des nœuds, des liens, et chaque champ que le modèle ne connaît pas, conservé pour être réécrit. Le tableau (canvasview.rs) dessine directement ce modèle : les cartes sous forme d’éléments, les liens et la grille peints en dessous. Chaque modification est écrite immédiatement, dans la mise en forme d’Obsidian — un nœud ou un lien par ligne —, et seulement si le texte change. Une écriture qui échoue marque l’onglet comme non enregistré. Le tableau relit son fichier quand celui-ci change sur le disque.

10. Git

kve-git fait ce que feraient status, diff, add et commit, directement sur le dépôt, avec gitoxide — rien ne dépend donc de la configuration du git installé. Là où gitoxide n’a pas de réponse, il lance git : récupération, envoi, rebasage, changement et création de branche.

11. Les tests

  • Des tests unitaires dans chaque crate, à côté du code.
  • Des tests de fenêtre dans kve-ui, grâce au test-support de gpui : une vraie fenêtre, sans compositeur. #[gpui::test], a_window_with(…) pour en ouvrir une, VisualTestContext pour la dessiner, et debug_bounds("sélecteur") pour savoir où un élément a été dessiné (les sélecteurs n’existent qu’en compilation de débogage). Les boîtes de dialogue de fichiers se répondent avec simulate_path_prompt_response et simulate_new_path_selection. Une reconstruction différée demande d’avancer l’horloge : cx.executor().advance_clock(…), puis run_until_parked().
  • Une vraie fenêtre : tools/drive.sh start | run | shot | stop lance l’éditeur sous cage, sans affichage, et le pilote avec kve-drive — déplacements, clics, glissements, raccourcis, saisie —, tandis que grim en fait des captures. make walkthrough enchaîne toutes les vérifications qui demandent une fenêtre.
  • La définition de « terminé » exige des tests des cas qui échouent, pas seulement de ceux qui fonctionnent.

12. Les performances

  • kve-bench mesure chaque opération pour laquelle un budget est fixé — ouvrir un fichier, le travail d’une frappe, une recherche, un formatage, l’écriture d’un tableau blanc — et échoue quand un 99e centile dépasse son budget. Les budgets viennent du §4 de la feuille de route : 16 ms pour tout ce qui se fait dans une image, 4 ms pour le traitement d’une saisie, davantage pour ce qui n’est demandé qu’une fois.
  • KVE_TIMINGS=1 fait écrire à l’éditeur une ligne par étape sur la sortie d’erreur : chaque étape de l’ouverture d’un projet, ainsi que la lecture, l’écriture et chaque image du tableau blanc.
  • Les chiffres sont conservés dans docs/perf/, une page par phase et une pour les séries de retours, avec ce qui a été mesuré, sur quoi, et ce qui a changé.

13. L’intégration continue

.github/workflows/ci.yml installe les bibliothèques système, puis lance le formatage, clippy, la documentation et les tests ; les tests propres à l’aperçu complet et une vérification de kve-preview avec son moteur ; puis les mesures de performance — les commandes de make check. Les mesures qui demandent une vraie fenêtre sont lancées à la main sur la machine de référence, pas en CI.

14. Le site web

Hugo, dans website/, en anglais et en français côte à côte (page.en.md, page.fr.md). La documentation utilisateur, dans content/docs/, compte une page par partie Diátaxis, comme ces pages pour les développeurs : Premiers pas (tutoriel), Guides pratiques, le Guide d’utilisation (référence — chaque fonction, une section chacune) et Comprendre l’éditeur (explication) ; le diataxis: de chaque page dit laquelle, et leurs poids les ordonnent. Trois autres pages de référence sont générées à partir des tables de l’éditeur lui-même par make site — tous les réglages, tous les raccourcis, le catalogue de la palette / — : elles ne peuvent pas diverger du code ; marquées annex: true, elles ne sont pas listées comme des pages à part : le chapitre Annexes du Guide d’utilisation y mène. chapter_depth: 2 donne à la carte du Guide d’utilisation les sections de chaque chapitre, sur sa ligne. Les captures d’écran viennent de tools/doc-shots.py, qui pilote l’éditeur sur un projet de démonstration, dans chaque langue. Une page marquée draft: true décrit la prochaine version et est publiée avec elle.

15. Publier une version

tools/release.sh check | prepare | finish <version> : check ne modifie rien ; prepare fait toutes les modifications d’une version et laisse les notes de version sous forme de squelette à rédiger ; finish lance les vérifications, commite, pose l’étiquette, construit l’archive, le .deb et le site. Il ne pousse, ne téléverse et ne déploie jamais rien — ces étapes sont affichées pour qu’une personne les fasse.

project.toml, à la racine, dit une seule fois ce que plusieurs fichiers diraient sinon chacun de leur côté : le nom, l’identifiant de l’application, le site, le dépôt, le site de l’auteur, le nom du développeur et le responsable du paquet. Le site le monte comme site.Data.project — le pied de page le lit, et une page écrit {{< project repository >}} ou {{< project raw "LICENSE.md" >}} plutôt qu’une adresse. release.sh et deb.sh le lisent par tools/project.sh <clé>. Cargo.toml et le fichier metainfo ne savent rien lire : make sync le leur recopie (prepare le lance), et un test échoue quand une copie diverge ou qu’une page réécrit l’adresse du dépôt.

16. Les fonctions, une par une

Chaque fonction ci-dessous est décrite de la même façon : le chemin, de la touche ou du menu jusqu’à ce qui change, les fichiers et les fonctions en jeu, comment elle marche, et ses tests et ses mesures. Les fonctions sont nommées comme dans le code ; window.rs désigne crates/kve-ui/src/window.rs, où la fenêtre de l’éditeur — EditorWindow — traite chaque commande.

16.0 Le chemin de toute commande

Une commande est déclarée une seule fois, dans kve_config::command::COMMANDS : son identifiant, sa portée (Fichier, Projet, Éditeur), la clé de traduction de son libellé, sa touche par défaut et son sous-menu. Les menus (menu.rs) et le clavier (window.rs, bindings) sont tous deux construits depuis cette table : une commande ne peut pas avoir de touche sans entrée de menu, ni de libellé sans traduction. Tous deux émettent une action gpui, qu’EditorWindow reçoit dans un gestionnaire enregistré par on_action dans son render.

flowchart LR reg["kve_config::command<br/>COMMANDS"] --> menu["menu.rs<br/>les menus"] reg --> keys["window.rs<br/>bindings"] menu --> act(["action gpui<br/>ex. CheckSpelling"]) keys --> act pane["ligne du panneau markdown<br/>ligne de la palette /"] --> act act --> handler["gestionnaire d'EditorWindow<br/>ex. check_spelling"] handler --> pure["fonction pure<br/>kve-markdown, kve-config…"] pure --> replace["replace_text<br/>une étape d'annulation"] replace --> editor["le tampon de l'éditeur<br/>et l'écran"]

La règle que suivent les fonctions : le calcul — ce que devient le document — est une fonction pure dans un crate sans fenêtre (kve-markdown, kve-config, kve-core), testée sans compositeur ; le gestionnaire lit le texte et la sélection, l’appelle, et réécrit le résultat par replace_text, qui fait du changement une seule étape d’annulation. Ce qui grandit avec le document tourne sur l’exécuteur d’arrière-plan (cx.background_spawn) et est abandonné si le texte a changé entre-temps.

16.1 Gras, italique, barré

  • Chemin : Bold, Italic, Strikethrough → bold / italic / strikethrough → emphasise(Emphasis, …) → kve_markdown::toggle_emphasis → replace_text, puis set_selected_range sur les mots.
  • Fichiers : crates/kve-markdown/src/emphasis.rs, window.rs.
  • Comment : toggle_emphasis(text, selection, what) rend le nouveau texte et la plage à sélectionner — toujours les mots, jamais les marques, ce qui permet à un second appui de défaire le premier. Trois cas : rien de sélectionné (marques écrites, curseur entre elles) ; marques dans la sélection (**mot** sélectionné en entier — retirées) ; marques juste autour (retirées aussi, sinon un second appui donnerait ****mot****). Que * soit le début de ** est pris en compte : l’italique dans un mot en gras s’ajoute au lieu de retirer. Une plage hors du texte ou qui coupe un caractère rend None plutôt que de paniquer.
  • Tests : emphasis::tests — huit, dont pressing_it_twice_puts_the_document_back et italic_inside_a_bold_word_adds_rather_than_removes.

16.2 Des listes qui se poursuivent seules

  • Chemin : Entrée dans le contexte Input de l’éditeur → enter_in_list (un gestionnaire de touche sur l’élément de l’éditeur, avant son propre retour à la ligne) → kve_markdown::enter_in_list(text, caret) → replace_text et le curseur déplacé.
  • Fichiers : crates/kve-markdown/src/list.rs, window.rs.
  • Comment : des lignes, pas une analyse — savoir dans quel élément est le curseur ne demande que sa ligne et celles du dessus. enter_in_list rend None hors d’un élément (l’éditeur coupe alors la ligne comme d’habitude), l’élément suivant pour un élément avec du texte (même marque, numéro suivant avec son délimiteur, [ ] dans une liste de tâches, la fin de ligne reportée), l’indentation et la marque du parent pour un élément vide imbriqué, et une ligne vide pour un élément vide au premier niveau. Les délimiteurs de code au-dessus du curseur sont comptés, pour qu’une ligne dans un bloc de code ne soit jamais prise pour un élément. Maj+Entrée n’est pas intercepté.
  • Tests : list::tests (huit) ; dans la fenêtre, enter_goes_on_with_a_list_then_up_and_out, shift_enter_in_a_list_only_breaks_the_line.
  • Mesuré : Entrée à la fin d’un document de 480 Ko, 1,2 ms p50, 1,5 ms p99, budget 4 ms (kve-bench).

16.3 Formater un fichier

  • Chemin : Format → format → formatter_for(index) choisit → markdown : reformat → kve_markdown::format sur le fil principal ; tout le reste : format_off_thread → kve_format::format (intégré) ou kve_core::external (un programme) sur l’exécuteur d’arrière-plan → le résultat réécrit en une seule modification, le curseur gardé.
  • Fichiers : crates/kve-markdown/src/format.rs (markdown, le greffon dprint plus la garde des indices), crates/kve-format/src/lib.rs (niveau 1 : swc par dprint-plugin-typescript, dprint-plugin-json, malva, markup_fmt, pretty_yaml, taplo, mago-formatter), crates/kve-core/src/external.rs (niveaux 2 et 3 : rustfmt, gofmt, shfmt, ruff, dprint).
flowchart TD f["formatter_for(document)"] -->|markdown| md["Embedded<br/>kve_markdown::format"] f -->|le projet a un dprint.json| dp["External<br/>dprint"] f -->|intégré pour ce langage| bi["Builtin<br/>kve_format::format"] f -->|programme installé| ex["External<br/>rustfmt, gofmt, shfmt, ruff"] f -->|aucun| no["message : aucun formateur"]
  • Comment : chaque formateur répond Ok(None) quand le texte est déjà formaté, si bien qu’un formatage qui ne change rien ne fait ni modification, ni étape d’annulation, ni écriture. Un texte qui ne s’analyse pas est une erreur, signalée et non écrite. tab_size, insert_spaces et line_width atteignent chaque formateur par FormatOptions / kve_format::Layout. En markdown, un ~indice~ est caché à dprint puis remis ; s’il ne peut pas l’être, le formatage est abandonné (ProtectionLost, F101). format_on_save passe par formats_on_save, le même choix par langage.
  • Tests : formatting_lines_up_a_ragged_table, formatting_keeps_the_cursor_where_it_was, format_on_save_writes_the_formatted_text_once, formatting_rust_runs_rustfmt, json_is_formatted_by_the_formatter_built_in, a_language_with_no_formatter_says_so ; ceux de kve-format, par langage.
  • Mesuré : 100 Ko de TypeScript, intégré, 42,7 ms p50 ; de CSS, 26,4 ms p50 (budget 120 ms, hors du fil principal) ; rustfmt sur 600 lignes par les tubes, 40 ms.

16.4 Vérifier l’orthographe

  • Chemin : CheckSpelling → check_spelling (bascule OpenDocument::spelling_on) → schedule_spelling(delay, say) → sur l’exécuteur d’arrière-plan : dictionary_for → prose_words → unknown_words (lance enchant-2) → misspelt → de retour sur le fil principal : underline_misspelt → les décorations de l’éditeur.
  • Fichiers : crates/kve-markdown/src/spelling.rs, window.rs.
sequenceDiagram participant U as Vous participant W as EditorWindow participant B as exécuteur d'arrière-plan participant E as enchant-2 U->>W: Fichier › Vérifier l'orthographe W->>B: schedule_spelling(texte) B->>B: dictionary_for(lang de l'en-tête, sinon la langue) B->>B: prose_words(texte) B->>E: chaque mot distinct, un par ligne E-->>B: les inconnus B-->>W: plages mal orthographiées W->>W: texte inchangé ? underline_misspelt U->>W: tape, s'arrête 120 ms W->>B: schedule_rebuild → schedule_spelling (silencieux)
  • Comment : prose_words parcourt les événements de pulldown-cmark et garde les mots des Text hors des blocs de code, des blocs HTML et des métadonnées, puis écarte ceux d’un wikilien ou de l’en-tête (une liste triée et une recherche dichotomique), un #mot-clé, et les mots contenant un chiffre ou un _. unknown_words donne à enchant-2 -l -d <dict> chaque mot distinct une seule fois, écrit depuis un autre fil pour qu’aucun tube ne se remplisse. Un programme absent est SpellError::NoChecker, un dictionnaire absent NoDictionary ; l’un comme l’autre arrête la vérification et le dit. Après chaque pause, schedule_rebuild (le même délai de 120 ms que l’aperçu) appelle schedule_spelling en silence ; un résultat pour un texte qui a changé depuis est abandonné. Les soulignements sont une collection de TextDecoration sur l’éditeur, ondulés, dans la couleur danger du thème.
  • Tests : spelling::tests — only_the_prose_is_offered, the_dictionary_is_the_documents_language, enchant_is_asked_when_there_is_one ; dans la fenêtre, check_spelling_underlines_and_clears, check_spelling_without_the_dictionary_says_so.
  • Mesuré : les mots d’un texte de 480 Ko, 11,7 ms p50 (budget 50 ms) ; enchant-2 sur ses 4 860 mots distincts, 30 ms. KVE_TIMINGS=1 affiche une ligne spelling par vérification.

16.5 La palette /

  • Chemin : chaque frappe dans un document markdown → kve_config::palette::typed(text, caret) (le mot sous le curseur est-il un /… ?) → matching(query) → le menu dessiné au curseur → Entrée, Tab ou un clic → choose → replace_text.
  • Fichiers : crates/kve-config/src/palette.rs (le catalogue, la recherche, ce qu’écrit une ligne), crates/kve-config/src/characters.rs (/symbol, /emoji, /entity), les modèles de resources/templates/ et du projet ; le menu dans window.rs (le champ slash, slash_act, palette_key).
  • Comment : la frappe ne valide jamais — typed répond None dès que le mot ne commence plus par /, si bien qu’une espace, un chemin ou une date ne l’ouvrent jamais ; une recherche vide est un menu fermé ; seul choose écrit. Deux niveaux pour parcourir (sections, puis lignes), une seule liste pour chercher. Le corps d’une ligne porte une marque de curseur, là où le curseur arrive. Voir La palette / pour le catalogue.
  • Tests : palette::tests, dont a_path_and_a_date_never_open_it, every_body_marks_one_caret ; dans la fenêtre, a_query_nothing_matches_closes_it_and_keeps_every_character.
  • Mesuré : le déclencheur sur un document de 1 Mo, moins de 0,001 ms ; une recherche avec caractères (/arr), 0,22 ms p50 (budget 4 ms).

16.6 Tableaux

  • Chemin : Insérer un tableau → insert_table → new_table_in → kve_markdown::insert_table(text, at, 3, 2), le curseur dans la première cellule d’en-tête (la ligne /table de la palette appelle la même) ; Ajouter une ligne / une colonne, Aligner → table_row, table_column, align_table → kve_markdown::table_at trouve le tableau sous le curseur, qui est modifié et réécrit aligné ; Modifier en grille → table_grid → grid.rs ouvre une fenêtre WindowKind::Dialog → Appliquer réécrit sur la plage mémorisée.
  • Fichiers : crates/kve-markdown/src/table.rs, crates/kve-ui/src/grid.rs, window.rs.
  • Comment : le tableau analysé est la vérité et les champs de la grille une vue de lui : chaque changement qui n’est pas une frappe (une ligne, une colonne, un alignement, un tri) va au tableau et les champs sont reconstruits ; la frappe va dans l’autre sens. La grille est modale, donc la plage d’octets mémorisée à l’ouverture est encore celle du tableau à Appliquer — vérifiée quand même. Un | échappé reste dans sa cellule ; un paragraphe avec des | n’est pas un tableau.
  • Tests : table::tests (dont an_escaped_pipe_stays_inside_its_cell) ; a_table_is_inserted_grown_and_lined_up, a_table_command_with_no_table_under_the_cursor_says_so.

16.7 La fenêtre des formules

  • Chemin : Formula → formula → kve_markdown::maths_at(text, caret) trouve le $…$ / $$…$$ autour du curseur (ou en ouvre un vide au curseur) → formula.rs ouvre une fenêtre de dialogue avec le MathEditor de ratex-gpui → Appliquer → le TeX réécrit exactement sur cette plage, sous la forme où il a été trouvé.
  • Fichiers : crates/kve-ui/src/formula.rs, crates/kve-ui/src/maths.rs, crates/kve-markdown/src/maths.rs.
  • Comment : le document ne contient jamais que du TeX ; la fenêtre tient un arbre tant qu’elle est ouverte. Appliquer sur une formule non touchée n’écrit rien, et une formule dont l’aller-retour par l’éditeur structurel n’est pas fidèle le dit avant toute frappe. La même touche, fenêtre ouverte, la ferme. Le même moteur RaTeX dessine les maths dans l’aperçu complet : ce qui est construit est ce qui sera montré.

16.8 Couleurs

  • Chemin : ToggleColourPane → toggle_colour_pane → en fenêtre (colour_as = "window", panewindow.rs) ou en panneau → Insérer → insert_colour → write_at_the_cursor(hex_of(colour)), ou colour_board quand un tableau blanc est au premier plan → remember_colour. À part, à chaque pause, schedule_swatches → kve_core::colours(text) → une pastille de fond sur chaque #.
  • Fichiers : crates/kve-core/src/colour.rs (trouver les couleurs), window.rs (colour_sliders, colour_slider_row, insert_colour, paint_the_colours), crates/kve-ui/src/panewindow.rs.
  • Comment : une couleur est #rgb, #rgba, #rrggbb ou #rrggbbaa, trouvée par une recherche d’octet sur # ; seul ce qui suit est lu ; &#160; est refusé (un # après & est une référence de caractère). Seul le # est peint, en fond : un premier plan se battrait avec la coloration syntaxique, et le vainqueur dépend de l’ordre d’un hachage en amont (F137). Les pastilles sont dessinées en CSS, JSON, markdown, XML (donc HTML et SVG) et Vue. La fenêtre écrit là où était le curseur dans la fenêtre d’éditeur à laquelle elle appartient ; l’opacité n’est écrite que si elle n’est pas pleine.
  • Tests : the_colour_pane_writes_its_colour_at_the_cursor, the_colour_key_opens_and_shuts_its_window, a_stylesheet_opens_with_a_chip_on_every_colour, the_colour_window_colours_the_selection_on_a_board ; colour::tests.
  • Mesuré : trouver les couleurs de 100 Ko, 0,023 ms p50 (budget 4 ms).

16.9 Caractères et émojis

  • Chemin : ToggleCharactersPane → toggle_characters_pane → open_characters (une fenêtre, characters_as) → un clic → le caractère, ou son entité, écrit au curseur → kve_config::characters::remember.
  • Fichiers : crates/kve-config/src/characters.rs (le catalogue et search), crates/kve-ui/src/emoji.rs (la police d’émojis en couleur fournie), window.rs.
  • Comment : un catalogue, deux portes — la fenêtre et les /symbol, /emoji, /entity de la palette appellent tous characters::search(query, kind, limit). Symboles et entités forment une table nommée en anglais et en français ; les émojis viennent du crate emojis avec leurs noms CLDR, en anglais seulement. La liste des derniers utilisés est gardée dans la session. Les émojis sont dessinés en couleur par la police NotoColorEmoji.ttf fournie, ajoutée aux polices de secours de chaque fenêtre.
  • Tests : characters::tests, characters_from_the_palette_and_the_pane, the_characters_as_a_window.
  • Mesuré : une recherche de la palette avec caractères, 0,22 ms p50.

16.10 Coller depuis l’historique

  • Chemin : Ctrl+C / Ctrl+X en route vers l’éditeur → copying → remember_clip ; PasteFromHistory → paste_from_history → la liste au curseur (clips) → clips_key → take_clip → collé, et remis en tête.
  • Fichiers : window.rs seulement.
  • Comment : un Vec<String> sur la fenêtre, le plus récent en premier, sans doublon, coupé à clipboard_history ; le texte actuel du presse-papiers système est mis en tête quand il vient d’un autre programme. Jamais écrit sur le disque.
  • Tests : paste_from_history_by_the_keys, clipboard_history_zero_keeps_nothing.

16.11 Historique local

  • Chemin : chaque enregistrement → kve_config::history::record (et record_disk avant le premier enregistrement, et quand un autre programme a modifié le fichier) ; ouverture d’un projet → prune_all ; LocalHistory → show_local_history → un onglet localhistory.rs → une version choisie → la vue des différences → Restaurer → restore_selected, par la fenêtre.
  • Fichiers : crates/kve-config/src/history.rs, crates/kve-ui/src/localhistory.rs, crates/kve-ui/src/diffview.rs.
  • Comment : ~/.local/share/kaivalya-editor/history/<hash>/, un dossier par fichier contenant le chemin du fichier et un <millisecondes>.snap par version — un dossier lisible avec ls et cat. Une version égale à la plus récente n’est pas gardée. Bornée par Keep { count, days }, appliqué quand un fichier est enregistré et à tous les fichiers à l’ouverture d’un projet. La restauration passe par la fenêtre, qui la refuse s’il y a des modifications non enregistrées.
  • Tests : history::tests (dont the_newest_by_count_and_by_age_are_kept, two_paths_that_share_a_hash_do_not_share_a_folder) ; the_local_history_keeps_every_save_and_puts_one_back.
  • Mesuré : garder une version de 100 Ko à l’enregistrement, 4,3 ms p50 (budget 16 ms).

16.12 Coller une capture d’écran

  • Chemin : PasteImage → paste_image → clipboard_image → write_image (dans paste_image_folder à côté du document, sous un nom libre) → un lien ![](…) au curseur. Des fichiers déposés → drop_files → les images, en markdown, sont liées par link_dropped (copiées à côté du document, sauf si elles sont déjà dessous) ; le reste est ouvert.
  • Fichiers : window.rs.
  • Comment : trois refus, chacun avec un message : pas d’image dans le presse-papiers, un document jamais enregistré (pas d’« à côté »), un dossier où l’on ne peut pas écrire. L’extension suit le format de l’image du presse-papiers.
  • Tests : pasting_with_nothing_on_the_clipboard_says_so, dropping_pictures_links_them_and_opens_the_rest.

16.13 Liens vers d’autres fichiers

  • Chemin : une frappe dans [[ ou ]( → kve_markdown::complete_path(base, typed, kind) → la liste au curseur (paths) → Entrée / Tab → un dossier y entre, un fichier termine le lien.
  • Fichiers : crates/kve-markdown/src/wikilink.rs (complete_path, wikilinks), window.rs.
  • Comment : ce qui est tapé jusqu’au dernier / désigne un dossier, parcouru depuis celui du document ; le reste est le début d’un nom dedans. Un read_dir par frappe, aucun index du projet. Les dossiers d’abord ; un fichier caché seulement après un point ; huit lignes à la fois.
  • Tests : a_path_is_written_from_the_folder_the_document_is_in, escape_shuts_the_completion_and_keeps_the_characters, a_rust_file_is_not_offered_completions.
  • Mesuré : 0,69 ms p99 pour un dossier de mille entrées.

16.14 Ouvrir un projet

  • Chemin : Ouvrir un dossier → open_folder → open_project(root) → l’ancien projet rangé (session écrite, autocommit vidé, recherche, état TODO et git abandonnés, l’index des fichiers abandonné) → kve_fs::Tree::open(root, rules) → watch(root, rules) → la session relue → apply_project_settings → les panneaux TODO et git relus s’ils sont à l’écran.
  • Fichiers : window.rs (open_project, watch, take_changes, mend_index), crates/kve-fs/src/tree.rs, crates/kve-fs/src/rules.rs, crates/kve-fs/src/watch.rs.
flowchart TD open["open_project(root)"] --> away["ranger l'ancien<br/>session · autocommit · index abandonné"] away --> tree["kve_fs::Tree::open<br/>le dossier racine lu, rien de plus"] tree --> watch["watch(root, rules)<br/>enregistré hors du fil principal"] tree --> session["session relue<br/>onglets, curseurs, replis"] session --> settings["apply_project_settings<br/>.kaivalya/settings.toml"] watch --> timer["take_changes, 4 fois par seconde"] timer --> mend["lignes de l'arbre · mend_index ·<br/>read_again_from_disk"]
  • Comment : l’arbre lit un dossier la première fois qu’on l’ouvre et jamais avant : un projet de 100 000 fichiers coûte son dossier racine. Les Rules (show_hidden, use_gitignore, .git/ toujours exclu) décident de ce qui est listé ; un changement de règles rejuge les noms déjà lus — sans disque —, relance l’observateur et abandonne l’index. L’observateur est notify derrière un Pipeline qui regroupe les événements ; la fenêtre recueille ce qui est stabilisé avec une minuterie plutôt qu’une attente bloquante, et applique chaque changement à l’arbre, à l’index des fichiers (FileIndex::apply) et à un document ouvert, qui est relu (read_again_from_disk, F237) sauf s’il a des modifications non enregistrées. Un flux d’événements perdu jette l’index plutôt que de s’y fier. Un projet qu’on ne peut pas observer (plus de surveillances inotify disponibles) s’ouvre quand même.
  • Tests : a_project_that_cannot_be_watched_still_opens, a_lost_event_stream_throws_the_index_away_rather_than_trusting_it, an_ignored_folder_is_watched_while_the_explorer_has_it_open, a_tab_with_unsaved_edits_keeps_them_when_the_disk_changes.
  • Mesuré : ouvrir un projet de 10 000 fichiers, 28,6 ms p50 (budget 60 ms) ; reconstruire 10 000 lignes d’explorateur, 1,4 ms. L’enregistrement de l’observateur prenait 0,6 à 1,1 s sur le fil principal avant d’en être sorti ; ce que la fenêtre attend maintenant à l’ouverture d’un projet est d’environ 6 ms.

16.15 L’explorateur, son filtre, et créer des fichiers

  • Chemin : un clic sur un dossier → Tree::expand (un read_dir) → rows reconstruites ; Afficher les fichiers cachés / ignorés → toggle_hidden_files, toggle_ignored_files → Tree::set_rules ; la case de filtre → filter_by → l’index des fichiers et la recherche floue → refilter → seulement les dossiers menant à une correspondance ; le menu d’une ligne → kve_fs::make::{file, folder, rename, trash}.
  • Fichiers : crates/kve-fs/src/tree.rs, crates/kve-fs/src/make.rs, crates/kve-search/src/index.rs, crates/kve-search/src/matcher.rs, window.rs.
  • Comment : le filtre cherche dans l’index des fichiers du projet, pas dans les lignes à l’écran, donc il trouve des fichiers dans des dossiers jamais ouverts ; un motif dans la case est lu comme un motif. La création refuse un chemin qui sort du projet (make::inside), n’écrase jamais (write_if_new), et la suppression va dans la corbeille du bureau (make::trash), qui sert d’annulation.
  • Tests : a_filtered_tree_is_the_folders_on_the_way_to_a_match, the_filter_reaches_a_file_the_tree_has_never_read, a_glob_in_the_filter_box_is_a_glob, the_keyboard_walks_the_filtered_rows_rather_than_the_tree, a_path_that_leaves_the_project_is_refused.

16.16 Trouver un fichier par son nom

  • Chemin : FindFile → find_file → open_the_picker → build_index s’il n’y en a pas (sur l’exécuteur d’arrière-plan) → chaque frappe → Matcher::best(index, query, limit) → Entrée → le fichier ouvert.
  • Fichiers : crates/kve-search/src/index.rs (FileIndex::build, apply), crates/kve-search/src/matcher.rs (le score de nucleo-matcher), window.rs.
  • Comment : l’index est la liste des chemins relatifs du projet, sous les règles de l’explorateur, construit la première fois qu’on le demande — le sélecteur, le filtre de l’explorateur, les wikiliens — et réparé ensuite par l’observateur ; il part avec le projet (F143). Un projet trop grand pour être listé le dit (is_truncated). Le matcher garde les tampons de travail de nucleo d’une frappe à l’autre ; la casse intelligente vient de CaseMatching::Smart.
  • Tests : the_picker_reads_the_project_once_and_finds_by_name, choosing_from_the_picker_opens_the_file_and_shuts_it, the_picker_is_a_toggle_and_leaves_the_keyboard_where_it_found_it.
  • Mesuré : indexer 10 000 fichiers, 15 ms p50 (budget 200 ms) ; une recherche floue dessus, 0,31 ms p50 (budget 4 ms).

16.17 Rechercher et remplacer dans le projet

  • Chemin : une frappe dans la case → schedule_search (avec un délai ; le drapeau d’abandon de la recherche précédente levé) → sur l’exécuteur d’arrière-plan kve_search::search(root, rules, query, filters, limits, cancel) → Results → le panneau. Tout remplacer → une confirmation → replace_all → kve_search::replace avec exactement les fichiers listés → les onglets ouverts relus → la recherche relancée.
  • Fichiers : crates/kve-search/src/content.rs, crates/kve-search/src/glob.rs, window.rs (schedule_search, replace_all, open_match).
sequenceDiagram participant P as panneau de recherche participant W as EditorWindow participant B as exécuteur d'arrière-plan P->>W: frappe W->>W: lever le drapeau d'abandon de l'ancienne recherche W->>B: après le délai : search(query, filters) B->>B: parcours sous les règles de la recherche, memchr par ligne, binaires et > 16 Mo sautés B-->>W: Results (fichiers, lignes, coupé ?) P->>W: Tout remplacer, confirmé W->>B: replace(fichiers listés, query, remplacement) B->>B: chaque fichier recherché de nouveau, écrit à côté, renommé par-dessus B-->>W: remplacés, sautés (non enregistrés ou modifiés)
  • Comment : pas de crates grep — le parcours d’ignore, memchr pour les lignes, un test de NUL pour les binaires. Les règles propres à la recherche excluent toujours ce que .gitignore exclut, sauf si ignorés est coché ; les motifs d’inclusion et d’exclusion élaguent le parcours au lieu de filtrer les résultats. Les résultats sont coupés à mille fichiers, cent correspondances par fichier et quatre cents caractères par ligne. Un remplacement recherche de nouveau chaque fichier en l’écrivant, saute un fichier ouvert avec des modifications non enregistrées ou modifié depuis, écrit dans un fichier temporaire et le renomme par-dessus l’original, en gardant ses permissions.
  • Tests : replacing_changes_what_was_listed_and_says_what_it_did, a_file_with_unsaved_changes_is_left_alone_by_a_replace, a_replace_that_was_cancelled_changes_nothing, the_filter_boxes_narrow_the_search ; ceux de kve-search.
  • Mesuré : une recherche sur 50 000 fichiers, 99 ms p50 (budget 1 000 ms) ; un projet avec sa sortie de compilation dedans, 14 ms.

16.18 Le plan et la table des matières

  • Chemin : à chaque pause (schedule_rebuild, 120 ms) → markdown : kve_markdown::outline(text) ; code : symbols::outline(language, text) (une requête tree-sitter par langage) → le panneau du plan. Générer ou mettre à jour la table des matières → table_of_contents → kve_markdown::update_table_of_contents → replace_text.
  • Fichiers : crates/kve-markdown/src/outline.rs, crates/kve-markdown/src/toc.rs, crates/kve-ui/src/symbols.rs.
  • Comment : le panneau du plan et la table des matières sont une seule lecture des titres, ils ne peuvent donc pas diverger. La table des matières vit entre <!-- toc --> et <!-- /toc --> : écrite au curseur la première fois, remplacée là où elle est ensuite, jusqu’à toc_max_level. Le plan du code utilise une requête @definition / @name par grammaire, à la façon des tags.scm de tree-sitter ; un test compile chaque requête contre sa grammaire.
  • Tests : opening_the_outline_lists_the_headings ; outline::tests, toc::tests, symbols::tests.

16.19 Rétroliens

  • Chemin : la section Rétroliens du panneau markdown ouverte, un changement de document ou un enregistrement → sur l’exécuteur d’arrière-plan, un parcours des fichiers markdown du projet → kve_markdown::mentions(source, from, target) par fichier → la section. Générer ou mettre à jour les rétroliens → update_mentions → kve_markdown::update_mentions → replace_text.
  • Fichiers : crates/kve-markdown/src/mentions.rs, window.rs.
  • Comment : une mention est un wikilien (inclusions comprises) ou un lien ou une image markdown, résolu comme un clic le résout : un lien depuis le dossier du fichier qui le contient ou la racine du projet, un wikilien à côté du fichier, depuis la racine, ou par son seul nom — si bien que [[Notes]] compte pour chaque Notes.md. Un chemin entre accents graves n’est pas un lien. Le bloc écrit se tient entre <!-- mentions --> et <!-- /mentions -->, chaque phrase citée dans un bloc de code en ligne pour que ses liens ne soient pas comptés la fois suivante.
  • Tests : the_mentions_are_listed_and_written ; mentions::tests.
  • Mesuré : les rétroliens d’une note sur 2 000 fichiers markdown, 204 ms p50 (budget 1 000 ms), hors du fil principal.

16.20 Le panneau TODO

  • Chemin : le panneau à l’écran, ou toute écriture dans le projet → look_for_todos → sur l’exécuteur d’arrière-plan kve_search::search(Query::todos(tags), …) — le parcours même de la recherche → todo_groups(tags, results) → le panneau ; todo.md lu et analysé par kve_markdown::board pour Mes tâches. Une case → tick_item ; la flèche → move_to_my_tasks.
  • Fichiers : crates/kve-search/src/content.rs (Query::tags, Query::todos, is_checklist_item), crates/kve-markdown/src/board.rs, crates/kve-markdown/src/inline.rs (le markdown d’une carte sur une ligne), window.rs (look_for_todos, todo_groups, my_tasks_rows, tick_item, move_to_my_tasks).
  • Comment : un mot-clé est un mot entier tel qu’écrit — TODO: et TODO(ana) oui, todo et MASTODON non — et une ligne à deux mots-clés n’est listée qu’une fois, sous le premier. Les listes de tâches sont une recherche littérale de [ ], puis is_checklist_item ne garde que les éléments en début de ligne dans du markdown. Le parcours ne tourne que tant que le panneau est à l’écran, et un nouveau parcours abandonne l’ancien entre deux fichiers. Cocher relit d’abord la ligne et refuse une ligne qui a changé ; c’est écrit dans l’onglet ouvert s’il y en a un, en une étape d’annulation. Déplacer une ligne marquée écrit la carte dans todo.md et retire le commentaire, ou toute la ligne, de son fichier.
  • Tests : the_todo_pane_lists_the_tagged_lines_of_the_project, my_tasks_are_the_projects_todo_file, my_tasks_draws_a_cards_markdown, a_task_is_moved_out_of_the_code_into_my_tasks, a_checklist_item_is_ticked_from_the_pane.
  • Mesuré : les mots-clés sur 50 000 fichiers, 105 ms p50 (budget 1 000 ms) ; le markdown d’une carte, 1,5 µs par ligne.

16.21 Le tableau des tâches

  • Chemin : Le tableau des tâches → show_board → un onglet boardview::BoardView qui lit le todo.md analysé par la fenêtre à chaque dessin → un glisser → move_card → kve_markdown::move_card → écrit dans l’onglet de todo.md s’il est ouvert, enregistré aussitôt ; + → add_card → kve_markdown::add_card ; la case d’une sous-tâche → tick_item.
  • Fichiers : crates/kve-markdown/src/board.rs (board, Card, Subtask, move_card, add_card), crates/kve-ui/src/boardview.rs.
  • Comment : chaque ## est une colonne, chaque élément de premier niveau une carte ; une tâche indentée sous une carte est sa sous-tâche (un niveau ; plus profond, c’est la même liste ; une ligne vide la termine). Un déplacement emporte les lignes indentées de la carte. Le tableau ne garde aucune copie du fichier et ne lit rien lui-même ; il suit le todo.md ouvert à chaque modification. Le texte d’une carte ne passe par la vue markdown de gpui-base que s’il contient un caractère markdown ; les sous-tâches sont repliées derrière leur compte, parce qu’une ligne dessinée coûte autant qu’une carte à chaque image.
  • Tests : board::tests (dont tasks_indented_under_a_card_are_its_subtasks), the_board_shows_todo_and_a_dragged_card_moves, a_card_shows_markdown_and_its_subtasks_tick.
  • Mesuré : déplacer ou ajouter une carte dans un todo.md de 500 cartes, 0,024 ms ; une image d’un tableau de 50 cartes à trois sous-tâches, 6 ms en texte simple et 12 ms tout en markdown (3,4 ms avant F238).

16.22 La note du jour et les modèles

  • Chemin : TodaysNote → todays_note → <daily_note_folder>/<AAAA-MM-JJ>.md → s’il manque, le modèle daily_note_template rempli et écrit par kve_fs::make::write_if_new → ouvert.
  • Fichiers : window.rs, crates/kve-config/src/palette.rs (les modèles comme lignes de la palette, les marqueurs {{…}}), resources/templates/ (fournis, copiés dans le dossier de configuration seulement s’ils n’y sont pas).
  • Comment : jamais d’écrasement — une note existante est ouverte. Les modèles sont cherchés dans le .kaivalya/templates/ du projet, puis dans ~/.config/kaivalya-editor/templates/ ; un nom sans réponse est signalé, et la note commence vide.

16.23 Les réglages d’un projet, et le fichier du projet

  • Chemin : Réglages du projet → open_project_settings → le fichier ouvert (créé s’il manque) ; à l’ouverture du projet et à l’enregistrement de ce fichier → apply_project_settings(root) → la table du projet fusionnée par-dessus la vôtre, par-dessus les valeurs par défaut. Écrire le fichier du projet → write_rule_file → kve_config::rules::rule_template() → .kaivalya/project.toml.
  • Fichiers : window.rs, crates/kve-config/src/settings.rs (Settings::parse, la fusion), crates/kve-config/src/rules.rs.
  • Comment : chaque option est cherchée dans le fichier du projet, puis le vôtre, puis defaults.toml : une nouvelle option n’oblige jamais à mettre à jour le fichier du projet. Une clé inconnue est nommée et le reste s’applique ; un fichier qui ne s’analyse pas laisse vos réglages intacts. Le fichier de règles est produit à partir des mêmes clés de traduction que l’aide du panneau markdown, chaque clé commentée : en écrire un dans un arbre ne change rien tant qu’aucune ligne n’est décommentée.

16.24 Sessions, lieux, et plusieurs fenêtres

  • Chemin : tout changement à retenir (un onglet, un curseur, un repli) → remember → session() → kve_config::session::Session::save_in → un fichier par racine de projet dans le dossier de données ; à l’ouverture du projet → load_in → surviving() (les fichiers supprimés écartés). Récents et favoris → kve_config::places. Nouvelle fenêtre → new_window.
  • Fichiers : crates/kve-config/src/session.rs, crates/kve-config/src/places.rs, window.rs.
  • Comment : rien n’est écrit dans le projet. Les sessions de projets qui n’existent plus sont élaguées (prune_in). Chaque fenêtre a son propre projet ; ouvrir un projet tenu par une autre fenêtre amène celle-ci au premier plan, parce que deux fenêtres écriraient chacune la session par-dessus l’autre. Quitter interroge chaque fenêtre tour à tour et s’arrête au premier Annuler.
  • Tests : the_lists_are_written_down_and_read_back, opening_a_file_puts_it_at_the_front_of_the_recent_list ; session::tests, places::tests.

16.25 Atteindre un panneau sans la souris

  • Chemin : F6 / Maj+F6 → focus_pane / focus_previous_pane → le panneau suivant affiché par un bord, dans l’ordre du rail, puis le texte → le gestionnaire de touches du panneau (flèches, Début, Fin, ← →, Entrée).
  • Fichiers : window.rs.
  • Comment : il ne s’arrête que sur un panneau à l’écran, jamais sur une icône du rail. Un panneau commence là où vous êtes — le plan sur le titre au-dessus du curseur, l’explorateur et les fichiers ouverts sur le fichier au premier plan. Entrée ouvre et rend le clavier au texte.
  • Tests : the_keyboard_goes_round_the_panes_that_are_drawn, the_files_pane_is_walked_from_the_keyboard.

16.26 Git : état, différences et commit

  • Chemin : toute écriture dans le projet, ou le panneau ouvert → read_git_if_stale → read_git → sur l’exécuteur d’arrière-plan kve_git::open → status, branch, upstream, remotes → le panneau (git_pane). Un double-clic → show_diff → kve_git::diff / hunks / side_by_side → un onglet diffview.rs. Committer → commit_git → kve_git::commit(repo, paths, message).
  • Fichiers : crates/kve-git/src/lib.rs, crates/kve-ui/src/diffview.rs, window.rs (git_pane, read_git, commit_git, show_diff).
flowchart LR pane["Panneau Git"] -->|lecture| gix["kve-git<br/>gitoxide dans le processus"] gix --> st["status · diff · commit<br/>log · graph · blame<br/>conflicts · resolve"] pane -->|réseau, branches| git["kve-git<br/>lance votre git"] git --> net["fetch · push · rebase<br/>switch · create_branch"]
  • Comment : tout ce qui lit ou committe passe par gitoxide, donc le git installé et sa configuration n’importent pas ; rien dans kve-git n’est assez rapide pour une image, si bien que la fenêtre l’appelle toujours hors du fil principal. commit indexe les chemins cochés (un fichier supprimé est retiré), prend l’auteur dans la configuration de git, et refuse un message vide ou un commit qui ne changerait rien. Les différences sont calculées contre HEAD avec trois lignes de contexte ; des changements proches partagent un bloc, et un remplacement est placé à côté de ce qu’il remplace.
  • Tests : le tests.rs de kve-git — dont only_the_files_chosen_are_committed, a_commit_that_cannot_be_made_says_why, the_diff_is_against_head_with_context ; dans la fenêtre, the_git_pane_lists_and_commits_what_is_ticked, enter_in_the_message_commits.
  • Mesuré : git status de ce dépôt, 2,3 ms p50 (budget 120 ms).

16.27 Git : branches et réseau

  • Chemin : le nom de la branche → branch_menu → une branche → switch_branch (les documents non enregistrés demandés d’abord) → kve_git::switch → les documents ouverts relus ou fermés. Nouvelle branche… → start_naming_a_branch → create_named_branch → kve_git::create_branch. Récupérer, Pousser, Rebaser (dessinés par git_actions) → run_network(Network::…) → kve_git::fetch, push, rebase → les documents réécrits relus (follow_the_rebase).
  • Fichiers : crates/kve-git/src/lib.rs, window.rs.
  • Comment : ces cinq-là lancent le git installé, donc les clés ssh, les gestionnaires d’identifiants et les proxys marchent comme dans un terminal ; les invites sont coupées (GIT_TERMINAL_PROMPT=0 et compagnie), si bien qu’un dépôt qui veut un mot de passe échoue avec la phrase de git au lieu de rester bloqué. switch refuse d’écraser une modification et nomme les fichiers ; push définit la branche suivie la première fois ; rebase refuse tant qu’autre chose est en cours, et un conflit lance git rebase --abort — il n’y a pas de bouton Continuer derrière. Une commande en cours est dessinée dans la couleur d’accent, et un second appui ne fait rien.
  • Tests : switching_writes_the_branch_and_back, switching_refuses_to_overwrite_and_carries_the_rest, push_and_fetch_through_git, a_rebase_that_conflicts_is_undone, a_rebase_refuses_when_it_should ; dans la fenêtre, switching_branch_rewrites_the_open_document, fetch_and_push_from_the_pane, rebase_from_the_pane.

16.28 Git : historique, graphe, blame et conflits

  • Chemin : Historique → show_history → kve_git::log(repo, 500) → un onglet gitviews::HistoryView → un commit → kve_git::details → un fichier → open_commit_diff → kve_git::diff_in_commit. Graphe → show_graph → kve_git::graph → gitviews. Historique des lignes → blame_file → kve_git::blame. Un fichier U double-cliqué → conflictview.rs → kve_git::conflicts → Garder notre version / leur version / les deux → kve_git::resolve → Marquer comme résolu → kve_git::mark_resolved.
  • Fichiers : crates/kve-git/src/lib.rs, crates/kve-ui/src/gitviews.rs, crates/kve-ui/src/conflictview.rs.
  • Comment : le graphe trie les commits topologiquement, les enfants d’abord même quand deux partagent la même seconde, et donne à chaque lignée une voie et une couleur. Le blame décrit la version du dernier commit, et ne dessine que les lignes à l’écran (F207), comme la vue des journaux. Un bloc en conflit est lu depuis les marqueurs du fichier, qui reste la vérité : un bloc corrigé à la main quitte simplement l’onglet de fusion. Un message vide sur un commit de fusion prend merge_message, celui que git a préparé.
  • Tests : the_log_is_newest_first_and_blame_names_each_line, the_graph_draws_a_branch_and_its_merge, the_topological_sort_puts_children_first, conflict_markers_are_read_and_resolved, a_conflicted_merge_is_resolved_and_committed ; dans la fenêtre, history_and_blame_open_windows.
  • Mesuré : le journal, 500 commits, 20 ms p50 ; le graphe de toutes les branches, 21 ms ; le blame d’un fichier de 22 000 lignes, 220 ms (budget 1 000 ms), tout hors du fil principal.

16.29 Committer tout seul

  • Chemin : un enregistrement → saved_for_autocommit (la règle lue par autocommit_rule dans .kaivalya/project.toml ; demandée une fois par ask_about_autocommit) → une minuterie, relancée à chaque enregistrement (autocommit_wait) → take_autocommit → sur l’exécuteur d’arrière-plan kve_git::autocommit(repo, paths) puis kve_git::push → commit_and_push. Quitter le projet, fermer ou quitter l’éditeur → autocommit_now.
  • Fichiers : crates/kve-git/src/lib.rs (autocommit), window.rs, crates/kve-config/src/session.rs (AutocommitAnswer : la réponse et la règle à laquelle elle a été donnée).
  • Comment : autocommit ne prend que les chemins enregistrés qui diffèrent de HEAD, leurs noms comme message, et rend None pendant une fusion, un rebasage ou un cherry-pick ; un fichier en conflit n’est jamais pris. La réponse à la question est gardée avec la règle à laquelle elle répondait : une règle changée redemande. À la fermeture de la fenêtre, le commit et l’envoi se font sur-le-champ et la fenêtre les attend ; à l’ouverture d’un autre projet, ils tournent hors du fil principal.
  • Tests : autocommit_takes_the_saved_files_that_changed ; an_autocommit_pushes_and_leaving_the_project_does_not_wait, closing_the_window_commits_and_pushes_what_was_waiting.
  • Mesuré : committer et pousser à la fermeture, un fichier, un dépôt nu sur le même disque, 24 à 31 ms (budget 100 ms) ; un dépôt de l’autre côté d’un réseau ajoute son propre aller-retour.

16.30 L’aperçu en direct

  • Chemin : TogglePreview / ToggleSplit → toggle_preview / toggle_split → le mode du document (edit, preview, split) → à chaque pause schedule_rebuild → la source, ses retours à la ligne simples joints (kve_markdown::join_soft_breaks), donnée au TextViewState::markdown de l’aperçu → la vue markdown de gpui-base. En vue partagée, un curseur qui change de section → preview_follows_the_caret.
  • Fichiers : window.rs, crates/kve-markdown/src/preview.rs, crates/kve-markdown/src/outline.rs.
  • Comment : l’aperçu en direct est la vue markdown de la boîte à outils elle-même : il ne coûte rien de plus et se redessine à chaque pause ; il suit le thème de l’éditeur et montre tel qu’écrit ce qu’il ne connaît pas ($…$, [!TIP], mermaid). Le curseur, le défilement et un clic sur un titre restent accordés par la correspondance titres-blocs du plan, celle qu’utilise un clic dans le plan. Rien n’est construit pour un aperçu qui n’est pas à l’écran.
  • Tests : nothing_is_built_for_a_preview_that_is_not_open, the_preview_follows_the_caret_from_heading_to_heading.

16.31 L’aperçu complet

  • Chemin : OpenHeavyPreview → open_heavy_preview → heavy::Preview lance kve-preview → à chaque pause schedule_heavy_preview → heavy_page → kve_markdown::page(source, PageOptions) (le HTML, la feuille de style choisie incluse, maths et diagrammes dessinés en SVG) → écrit sur l’entrée standard du processus → kve-preview dessine la dernière page avec Blitz.
flowchart LR subgraph editor["kaivalya-editor"] src["le document"] --> page["kve_markdown::page<br/>to_html · feuille de style · inclusions"] page --> svg["maths : ratex → SVG<br/>mermaid : mermaid-rs-renderer → SVG"] svg --> pipe["heavy::Preview<br/>une page par image sur stdin"] end pipe --> preview["kve-preview<br/>Blitz : html5ever · Stylo · Vello"] preview --> win(["sa propre fenêtre"])
  • Fichiers : crates/kve-ui/src/heavy.rs (ce qui est envoyé, et le processus), crates/kve-preview/src/main.rs (le moteur de rendu), crates/kve-markdown/src/page.rs, crates/kve-markdown/src/html.rs, crates/kve-ui/src/maths.rs.
  • Comment : l’aperçu est nourri d’une page ; il ne lit jamais de fichier et ne demande rien à l’éditeur, si bien qu’en faire un panneau plus tard est un changement de transport. Deux verrous : la fonctionnalité cargo heavy-preview décide si la compilation contient un moteur de rendu et de diagrammes, heavy_preview si une compilation qui les a les propose. Un tube qui refuse une page veut dire que le processus est parti : la poignée est abandonnée et la barre d’état le dit. La page est celle de l’export, à une ligne <base> près, qui permet à la fenêtre de trouver vos images sur le disque.
  • Tests : the_heavy_preview_draws_markdown_and_html_and_nothing_else, the_heavy_preview_says_it_is_off_rather_than_doing_nothing ; page::tests.
  • Mesuré : la page de 100 Ko de markdown, 17 ms p50 (budget 120 ms) ; vingt formules en SVG, 6 ms ; un organigramme mermaid de six nœuds, 56 ms (551 ms la première fois). Le processus de l’aperçu occupe environ 117 Mo tant qu’il est ouvert.

16.32 Avec quelle feuille de style un document est dessiné

  • Chemin : un changement de document, d’en-tête ou de règle → refresh_markdown_rules → kve_markdown::stylesheet_for(document, source, chosen, stop) → la pastille du pied de fenêtre (stylesheet_chip) et la page. La pastille → switch_stylesheet → le choix gardé dans la session.
  • Fichiers : crates/kve-markdown/src/stylesheet.rs (stylesheet_for, folder_rule), crates/kve-ui/src/styles.rs (le dossier des styles, les feuilles fournies écrites), window.rs.
  • Comment : quatre réponses dans l’ordre — votre choix pour ce document, le css: de l’en-tête, la règle [[css]] la plus longue qui correspond dans .kaivalya/project.toml (la remontée s’arrête à la racine du projet), la feuille GitHub fournie. Un fichier nommé qui n’existe pas reste la réponse, pour qu’une faute de frappe soit signalée plutôt que cachée. Les réponses sont mises en cache : le pied de fenêtre les lit à chaque image, et aucun appel système n’a sa place dans le dessin. La lecture de l’en-tête est bornée ; la partie qui touche aux fichiers ne tourne que quand cette lecture change.
  • Tests : stylesheet::tests — choosing_the_bundled_one_beats_the_front_matter, the_longest_path_wins_and_the_projects_styles_come_first ; dans la fenêtre, the_switcher_offers_the_stylesheets_in_the_project, only_a_rendered_file_has_a_stylesheet_to_argue_about.

16.33 Exporter en HTML

  • Chemin : Exporter en HTML… → export_html → la boîte de dialogue (images, dessin par le navigateur, boutons de retour, feuille de style) → export_html_page → heavy_page_for_export(scripts, back_button) → kve_markdown::with_images (images en URI de données, ou copiées à côté) → écrit. Exporter le projet… → export_project_asked → un dossier → export_project_into → la même chose par fichier markdown, les liens entre pages réécrits en .html.
  • Fichiers : window.rs, crates/kve-markdown/src/export.rs, crates/kve-markdown/src/page.rs.
  • Comment : l’export est la page de l’aperçu complet sans sa ligne <base>. Dessinée ici, la page ne nomme aucun script ; laissée au navigateur, elle garde le TeX et la source mermaid et nomme MathJax et mermaid.js par export_mathjax_url / export_mermaid_url, entre guillemets pour qu’une adresse ne puisse pas sortir de son attribut. Deux images de même nom ne deviennent pas une. L’export du projet parcourt sous les règles de git et laisse de côté le dossier où il écrit ; un lien vers un fichier qui n’est pas une page reste tel qu’écrit, et il est compté.
  • Tests : the_export_asks_before_it_writes, an_export_is_one_file_with_its_pictures_inside_it, an_export_can_put_its_pictures_beside_it_instead, the_second_export_hands_the_maths_and_the_diagram_to_the_browser, a_project_is_exported_page_by_page, an_export_takes_the_stylesheet_chosen_for_it ; export::tests, page::tests.

16.34 Comment le markdown est lu

  • Chemin : chaque lecture d’un document — l’aperçu, la page, le plan, l’orthographe — passe par kve_markdown::reading::options(), l’unique jeu d’options de pulldown-cmark ; to_html_with(source, HtmlOptions) produit le HTML.
  • Fichiers : crates/kve-markdown/src/reading.rs, html.rs, callout.rs (les encadrés), wikilink.rs, transclude.rs (les inclusions, avec une garde contre une note qui s’inclut elle-même), maths.rs, mermaid.rs, tag.rs, front_matter.rs.
  • Comment : une seule lecture, si bien que le plan, la table des matières, l’aperçu et l’export ne peuvent pas diverger sur ce que contient un document. Les titres portent les ancres que distribue le plan, deux titres identiques recevant deux ancres distinctes. Les notes de bas de page sont rassemblées où qu’elles soient écrites. Les maths sont trouvées par maths() selon les règles du document de référence ; un bloc de code n’est jamais des maths.
  • Tests : html::tests (dont headings_carry_the_anchors_the_outline_handed_out), callout::tests, transclude::tests, maths::tests ; les tests du document de référence dans crates/kve-markdown/tests/reference_document.rs.
  • Mesuré : 100 Ko de markdown en HTML, 19 ms p50 (budget 120 ms) ; y trouver les maths, 0,85 ms.

16.35 La vue journal

  • Chemin : un fichier .log ouvert, ou Afficher comme journal → view_as_log → un onglet logview.rs → kve_core::log::LineIndex::of(path) (où commence chaque ligne) → les lignes à l’écran lues dans le fichier au moment de les dessiner. Un filtre, un niveau ou un intervalle de temps → un passage sur le fichier sur l’exécuteur d’arrière-plan → les lignes gardées. Aller à l’heure… → kve_core::log::first_at. Une minuterie chaque seconde → LineIndex::extend → ce qui a été ajouté est indexé et filtré à part.
  • Fichiers : crates/kve-core/src/log.rs (level_of, stamp_end, moment_of, bound, first_at, LineIndex), crates/kve-ui/src/logview.rs.
  • Comment : le fichier n’est jamais chargé — huit octets par ligne pour l’index, un petit cache des lignes dessinées. Un niveau est un mot près du début de la ligne, sous l’une de ses graphies habituelles ; une ligne qui n’en a pas (une trace de pile) prend celui de la précédente. Un moment est lu comme un nombre qui se trie (aaaammjjhhmmssmmm), si bien qu’un intervalle est le passage même du filtre, et Aller à l’heure coupe l’index en deux, en lisant quelques lignes à chacune d’une vingtaine d’étapes — d’où l’hypothèse d’un fichier dans l’ordre du temps. Un fichier devenu plus court est relu depuis le début.
  • Tests : a_log_is_filtered_followed_and_opened_as_text, a_log_is_searched_by_dates ; log::tests.
  • Mesuré : indexer un journal de 50 Mo et 500 000 lignes, 7,6 ms p50 (budget 500 ms) ; le filtrer sur un mot, 158 ms (budget 1 000 ms, hors du fil principal) ; aller à une heure, 0,65 ms.

16.36 Tableaux blancs

  • Chemin : un .canvas ouvert → kve_canvas::parse → un onglet canvasview.rs qui dessine le Canvas → chaque geste décidé d’après la géométrie du tableau (kve_canvas::geometry) → le modèle modifié (kve_canvas::edit) → kve_canvas::to_json écrit aussitôt s’il diffère. Exporter en SVG… → kve_canvas::svg::to_svg. Nouveau tableau blanc → new_whiteboard ; Afficher comme tableau blanc → show_whiteboard.
  • Fichiers : crates/kve-canvas/src/lib.rs (le modèle, lu et écrit), geometry.rs (ce qui est sous le pointeur, les côtés, les courbes), edit.rs, svg.rs ; crates/kve-ui/src/canvasview.rs.
flowchart LR file["fichier .canvas"] -->|parse| model["Canvas<br/>nœuds · arêtes · champs inconnus gardés"] model --> view["canvasview.rs<br/>cartes en éléments, arêtes peintes"] view -->|geste| geo["geometry<br/>ce qui est sous le pointeur"] geo --> edit["edit<br/>déplacer · redimensionner · relier · grouper"] edit --> model model -->|to_json, s'il a changé| file model -->|to_svg| svg["image.svg"]
  • Comment : le modèle garde chaque champ qu’il ne connaît pas et le réécrit tel quel, dans la disposition d’Obsidian d’un nœud ou d’une arête par ligne, si bien qu’un changement de carte est le changement d’une ligne dans git. Les cartes hors de l’écran ne sont pas construites ; en dessous d’une taille où ses mots feraient quelques pixels, une carte ne dessine que son cadre, et elle n’offre ses connecteurs et sa poignée qu’à partir de 48 × 32 pixels à l’écran. Une écriture qui échoue garde le changement sur le tableau et marque l’onglet non enregistré. Le tableau relit son fichier quand il change sur le disque.
  • Tests : a_canvas_opens_on_the_board_and_what_is_dropped_is_written, a_window_started_on_a_canvas_shows_the_board, the_board_exports_as_one_svg, the_text_colour_setting_reaches_the_board_and_its_picture ; ceux de kve-canvas.
  • Mesuré : écrire un tableau de 2 000 cartes, 2,4 ms p50 (budget 16 ms) ; le lire, 4,6 ms ; ce qui est sous un appui, 0,2 ms ; l’export SVG, 23 ms. KVE_TIMINGS=1 affiche la lecture, les écritures et les images du tableau.

16.37 Les panneaux, le rail et les panneaux en fenêtre

  • Chemin : un clic sur une icône du rail → reveal(pane) ou le bord replié ; un survol → peek(pane) (si peek_on_hover) ; un panneau montré en fenêtre (colour_as, characters_as) → toggle_pane_window → panewindow.rs.
  • Fichiers : window.rs (Pane, reveal, peek, side_of, open_on), crates/kve-ui/src/panewindow.rs, crates/kve-ui/src/icon.rs.
  • Comment : chaque bord montre un panneau ; le bord d’un panneau est son réglage <panneau>_side, et les icônes suivent rail_order (une liste pour les deux bords ; noms inconnus ignorés, répétitions comptées une fois, panneaux non listés à la suite). Un survol dessine le panneau par-dessus le texte et masque ce qui est dessous ; un glisser qui y commence le garde ouvert jusqu’au relâchement. Une fenêtre de panneau dessine le contenu même du panneau de la fenêtre d’éditeur — pas de seconde copie de l’état — et rend ce qu’elle écrit à la fenêtre qui tient le document.
  • Tests : the_rail_keeps_every_icon_and_pushes_the_open_one_in, the_rail_follows_the_order_it_is_given, hovering_an_icon_peeks_the_pane_over_the_editor, a_rail_told_not_to_peek_does_not, a_drag_out_of_a_peek_keeps_it_open_until_the_release, an_edge_folds_to_its_rail_and_comes_back.

16.38 Langages et coloration

  • Chemin : un fichier ouvert → son langage d’après son nom → la grammaire tree-sitter de la boîte à outils → les captures associées aux jetons du thème par syntax.rs. Le langage du pied de fenêtre, ou Langage… → choose_language_action → choose_language → les couleurs, le plan, le panneau markdown et le formateur suivent.
  • Fichiers : crates/kve-ui/src/syntax.rs, crates/kve-ui/src/code.rs, window.rs.
  • Comment : rien ne regarde dans un fichier pour deviner ; le nom décide, et un choix dans le pied de fenêtre le corrige pour ce fichier tant qu’il est ouvert (un Enregistrer sous l’efface). Une capture que le thème ne nomme pas est dessinée dans la couleur ordinaire, si bien qu’un manque ressemble à une grammaire qui ne colore pas. Le HTML est coloré avec la grammaire XML : la grammaire HTML fournie n’analyse pas le HTML (F119).
  • Tests : a_language_chosen_overrides_the_name ; syntax::tests, code::tests.

16.39 Thèmes et polices

  • Chemin : au démarrage et à un changement de réglages → theme::install(setting) → apply_configured ("light", "dark", ou apply_named) → set_fonts → settled : les polices de l’interface et du code vérifiées contre ce qui est installé (available_fonts, installed, has_bold). Chaque couleur que dessine l’éditeur vient de theme::tokens(cx).
  • Fichiers : crates/kve-ui/src/theme.rs, resources/themes/*.json (écrits dans ~/.config/kaivalya-editor/themes/ au démarrage), crates/kve-ui/src/emoji.rs.
  • Comment : un thème est un fichier JSON au format de la boîte à outils, avec un schéma à côté. Une police absente est remplacée ici, avant que le framework ne le fasse, pour que la chaîne de secours de la fenêtre (la police d’émojis en couleur) survive (F171) ; le remplacement prend la première police de secours qui a son propre gras, parce qu’une police variable est dessinée à une seule graisse (F242). Une police nommée dans les réglages est gardée telle quelle ; une à laquelle rien ne répond est signalée dans la barre d’état.
  • Tests : theme::tests — dont a_missing_family_becomes_the_one_the_framework_would_draw, a_fallback_with_a_bold_is_taken_first ; every_theme_round_trips_through_the_file.
  • Mesuré : au plus vingt recherches de police, une fois, au démarrage.

16.40 Le fichier de réglages

  • Chemin : Ctrl+, → open_settings → le fichier annoté écrit dans votre langue s’il manque, mis à jour avec toute nouvelle option en gardant vos valeurs (l’ancien en settings.toml.bak) → ouvert. Au démarrage → Settings::parse(votre fichier) fusionné par-dessus resources/settings/defaults.toml. Réinitialiser → reset_settings, après avoir demandé.
  • Fichiers : crates/kve-config/src/settings.rs, resources/settings/defaults.toml, resources/settings/for-tests.toml, crates/kve-config/locales/*.yml (le commentaire de chaque option, settings.*).
  • Comment : Settings::parse est la seule porte : un fichier partiel est fusionné par-dessus la table des valeurs par défaut, si bien qu’une option ajoutée plus tard n’oblige jamais à mettre votre fichier à jour. Une clé inconnue est signalée et le reste s’applique ; un fichier cassé est une erreur à la lecture mais n’arrête jamais l’éditeur. Les tests tournent sur for-tests.toml, dont les valeurs ne bougent pas, et n’affirment jamais ce que dit defaults.toml (F139). Voir Guides pratiques pour ajouter une option.
  • Tests : every_setting_is_explained_in_every_shipped_locale, the_two_settings_files_name_the_same_keys, a_partial_file_fills_the_rest_from_the_defaults, an_unknown_key_is_reported_rather_than_silently_ignored, a_file_written_before_an_option_existed_gains_it_and_keeps_what_it_had, the_settings_example_in_the_readme_actually_parses ; dans la fenêtre, the_settings_command_writes_the_annotated_file_and_opens_it, resetting_the_settings_asks_first_and_keeps_the_old_file.