Aller au contenu

Référence de la thématisation

Ce document décrit le fonctionnement de la thématisation dans le coding-agent aujourd’hui : schéma, chargement, comportement à l’exécution et modes de défaillance.

Le système de thèmes pilote :

  • les jetons de couleur premier plan/arrière-plan utilisés dans l’ensemble du TUI
  • les adaptateurs de style markdown (getMarkdownTheme())
  • les adaptateurs de sélecteur/éditeur/liste de paramètres (getSelectListTheme(), getEditorTheme(), getSettingsListTheme())
  • le préréglage de symboles + les surcharges de symboles (unicode, nerd, ascii)
  • les couleurs de coloration syntaxique utilisées par le surligneur natif (@f5-sales-demo/pi-natives)
  • les couleurs des segments de la barre d’état

Implémentation principale : src/modes/theme/theme.ts.

Les fichiers de thème sont des objets JSON validés par rapport au schéma d’exécution dans theme.ts (ThemeJsonSchema) et reflétés par src/modes/theme/theme-schema.json.

Champs de niveau supérieur :

  • name (requis)
  • colors (requis ; tous les jetons de couleur sont requis)
  • vars (optionnel ; variables de couleur réutilisables)
  • export (optionnel ; couleurs d’export HTML)
  • symbols (optionnel)
    • preset (optionnel : unicode | nerd | ascii)
    • overrides (optionnel : surcharges clé/valeur pour SymbolKey)

Les valeurs de couleur acceptent :

  • chaîne hexadécimale ("#RRGGBB")
  • index de couleur 256 (0..255)
  • chaîne de référence de variable (résolue via vars)
  • chaîne vide ("") signifiant la valeur par défaut du terminal (\x1b[39m premier plan, \x1b[49m arrière-plan)

Tous les jetons ci-dessous sont requis dans colors.

accent, border, borderAccent, borderMuted, success, error, warning, muted, dim, text, thinkingText

selectedBg, userMessageBg, customMessageBg, toolPendingBg, toolSuccessBg, toolErrorBg, statusLineBg

userMessageText, customMessageText, customMessageLabel, toolTitle, toolOutput

mdHeading, mdLink, mdLinkUrl, mdCode, mdCodeBlock, mdCodeBlockBorder, mdQuote, mdQuoteBorder, mdHr, mdListBullet

toolDiffAdded, toolDiffRemoved, toolDiffContext, syntaxComment, syntaxKeyword, syntaxFunction, syntaxVariable, syntaxString, syntaxNumber, syntaxType, syntaxOperator, syntaxPunctuation

thinkingOff, thinkingMinimal, thinkingLow, thinkingMedium, thinkingHigh, thinkingXhigh, bashMode, pythonMode

statusLineSep, statusLineModel, statusLinePath, statusLineGitClean, statusLineGitDirty, statusLineContext, statusLineSpend, statusLineStaged, statusLineDirty, statusLineUntracked, statusLineOutput, statusLineCost, statusLineSubagents

Utilisée pour les aides à la thématisation de l’export HTML :

  • export.pageBg
  • export.cardBg
  • export.infoBg

Si omis, le code d’export dérive les valeurs par défaut à partir des couleurs résolues du thème.

  • symbols.preset définit un jeu de symboles par défaut au niveau du thème.
  • symbols.overrides peut surcharger des valeurs individuelles de SymbolKey.

Ordre de priorité à l’exécution :

  1. surcharge symbolPreset des paramètres (si définie)
  2. symbols.preset du JSON du thème
  3. valeur de repli "unicode"

Les clés de surcharge invalides sont ignorées et journalisées (logger.debug).

Ordre de recherche des thèmes (loadThemeJson) :

  1. thèmes intégrés embarqués (defaults/xcsh-dark.json et defaults/xcsh-light.json compilés dans defaultThemes)
  2. fichier de thème personnalisé : <customThemesDir>/<name>.json

Le répertoire des thèmes personnalisés provient de getCustomThemesDir() :

  • par défaut : ~/.xcsh/agent/themes
  • surchargé par PI_CODING_AGENT_DIR ($PI_CODING_AGENT_DIR/themes)

getAvailableThemes() retourne les noms fusionnés intégrés + personnalisés, triés, avec priorité aux intégrés en cas de collision de noms.

Pour les fichiers de thèmes personnalisés :

  1. lecture du JSON
  2. analyse du JSON
  3. validation par rapport au ThemeJsonSchema
  4. résolution récursive des références vars
  5. conversion des valeurs résolues en ANSI selon le mode de capacité du terminal

Comportement de validation :

  • jetons de couleur requis manquants : message d’erreur groupé explicite
  • types/valeurs de jetons incorrects : erreurs de validation avec chemin JSON
  • fichier de thème inconnu : Theme not found: <name>

Comportement des références de variables :

  • prend en charge les références imbriquées
  • lève une exception en cas de référence de variable manquante
  • lève une exception en cas de références circulaires

Détection du mode de couleur (detectColorMode) :

  • COLORTERM=truecolor|24bit => truecolor
  • WT_SESSION => truecolor
  • TERM dans dumb, linux, ou vide => 256color
  • sinon => truecolor

Comportement de conversion :

  • hex -> Bun.color(..., "ansi-16m" | "ansi-256")
  • numérique -> ANSI 38;5 / 48;5
  • "" -> réinitialisation premier plan/arrière-plan par défaut

main.ts initialise le thème avec les paramètres :

  • symbolPreset
  • colorBlindMode
  • theme.dark
  • theme.light

La sélection automatique du créneau de thème utilise la détection de l’arrière-plan via COLORFGBG :

  • analyse de l’index d’arrière-plan depuis COLORFGBG
  • < 8 => créneau sombre (theme.dark)
  • >= 8 => créneau clair (theme.light)
  • échec de l’analyse => créneau sombre

Valeurs par défaut actuelles du schéma de paramètres :

  • theme.dark = "xcsh-dark"
  • theme.light = "xcsh-light"
  • symbolPreset = "unicode"
  • colorBlindMode = false
  • charge le thème sélectionné
  • met à jour le singleton global theme
  • démarre optionnellement l’observateur
  • déclenche le callback onThemeChange

En cas d’échec :

  • retombe sur le thème intégré dark
  • retourne { success: false, error }
  • applique temporairement le thème de prévisualisation au theme global
  • ne modifie pas les paramètres persistés en lui-même
  • retourne succès/erreur sans remplacement de repli

L’interface des paramètres utilise ceci pour la prévisualisation en direct et restaure le thème précédent en cas d’annulation.

Lorsque l’observateur est activé (setTheme(..., true) / initialisation interactive) :

  • observe uniquement le chemin du fichier personnalisé <customThemesDir>/<currentTheme>.json
  • les thèmes intégrés ne sont effectivement pas observés
  • change du fichier : tente un rechargement (avec anti-rebond)
  • rename/suppression du fichier : retombe sur dark, ferme l’observateur

Le mode automatique installe également un écouteur SIGWINCH et peut réévaluer le mappage du créneau sombre/clair lorsque l’état du terminal change.

colorBlindMode ne modifie qu’un seul jeton à l’exécution :

  • toolDiffAdded est ajusté en HSV (le vert est décalé vers le bleu)
  • l’ajustement n’est appliqué que lorsque la valeur résolue est une chaîne hexadécimale

Les autres jetons restent inchangés.

Les paramètres liés aux thèmes sont persistés par Settings dans le fichier YAML de configuration globale :

  • chemin : <agentDir>/config.yml
  • répertoire agent par défaut : ~/.xcsh/agent
  • fichier effectif par défaut : ~/.xcsh/agent/config.yml

Clés persistées :

  • theme.dark
  • theme.light
  • symbolPreset
  • colorBlindMode

Une migration héritée existe : l’ancien format plat theme: "name" est migré vers le format imbriqué theme.dark ou theme.light basé sur la détection de luminance.

  1. Créez un fichier dans le répertoire des thèmes personnalisés, par exemple ~/.xcsh/agent/themes/my-theme.json.
  2. Incluez name, des vars optionnels, et tous les jetons colors requis.
  3. Incluez optionnellement symbols et export.
  4. Sélectionnez le thème dans les Paramètres (Affichage -> Thème sombre ou Affichage -> Thème clair) selon le créneau automatique souhaité.

Squelette minimal. Chaque clé dans colors est requise — le validateur à l’exécution (additionalProperties: false) rejette à la fois les clés manquantes et les clés inconnues. Pour les implémentations de référence fournies, consultez packages/coding-agent/src/modes/theme/defaults/xcsh-dark.json et xcsh-light.json.

La barre d’état possède deux systèmes de couleurs parallèles documentés dans l’issue #242 :

  • Les couleurs de texte hexadécimales (statusLinePath, statusLineGitClean, statusLineGitDirty, statusLineStaged, statusLineDirty, statusLineUntracked) pilotent le rendu non-powerline.
  • Les indices de palette 256 couleurs (statusLine<Segment>Bg / statusLine<Segment>Fg) pilotent le remplissage des segments powerline. Ils sont indépendants des clés hexadécimales ci-dessus — les deux doivent être définis.
{
"name": "my-theme",
"vars": {
"accent": "#7aa2f7",
"muted": 244
},
"colors": {
"accent": "accent",
"chromeAccent": "accent",
"spinnerAccent": "accent",
"contentAccent": "muted",
"border": "#4c566a",
"borderAccent": "accent",
"borderMuted": "muted",
"success": "#9ece6a",
"error": "#f7768e",
"warning": "#e0af68",
"muted": "muted",
"dim": 240,
"gutterSuccess": "#7dcfff",
"gutterWarning": "#e0af68",
"text": "",
"thinkingText": "muted",
"selectedBg": "#2a2f45",
"userMessageBg": "#1f2335",
"userMessageText": "",
"customMessageBg": "#24283b",
"customMessageText": "",
"customMessageLabel": "accent",
"toolPendingBg": "#1f2335",
"toolSuccessBg": "#1f2d2a",
"toolErrorBg": "#2d1f2a",
"toolTitle": "",
"toolOutput": "muted",
"mdHeading": "accent",
"mdLink": "accent",
"mdLinkUrl": "muted",
"mdCode": "#c0caf5",
"mdCodeBlock": "#c0caf5",
"mdCodeBlockBorder": "muted",
"mdQuote": "muted",
"mdQuoteBorder": "muted",
"mdHr": "muted",
"mdListBullet": "accent",
"toolDiffAdded": "#9ece6a",
"toolDiffRemoved": "#f7768e",
"toolDiffContext": "muted",
"syntaxComment": "#565f89",
"syntaxKeyword": "#bb9af7",
"syntaxFunction": "#7aa2f7",
"syntaxVariable": "#c0caf5",
"syntaxString": "#9ece6a",
"syntaxNumber": "#ff9e64",
"syntaxType": "#2ac3de",
"syntaxOperator": "#89ddff",
"syntaxPunctuation": "#9aa5ce",
"syntaxControl": "#bb9af7",
"thinkingOff": 240,
"thinkingMinimal": 244,
"thinkingLow": "#7aa2f7",
"thinkingMedium": "#2ac3de",
"thinkingHigh": "#bb9af7",
"thinkingXhigh": "#f7768e",
"bashMode": "#2ac3de",
"pythonMode": "#bb9af7",
"statusLineBg": "#16161e",
"statusLineSep": 240,
"statusLineModel": "#bb9af7",
"statusLinePath": "#7aa2f7",
"statusLineGitClean": "#9ece6a",
"statusLineGitDirty": "#e0af68",
"statusLineContext": "#2ac3de",
"statusLineSpend": "#7dcfff",
"statusLineStaged": "#9ece6a",
"statusLineDirty": "#e0af68",
"statusLineUntracked": "#f7768e",
"statusLineOutput": "#c0caf5",
"statusLineCost": "#ff9e64",
"statusLineSubagents": "#bb9af7",
"statusLineOsIconBg": 7,
"statusLineOsIconFg": 232,
"statusLinePathBg": 4,
"statusLinePathFg": 254,
"statusLineGitCleanBg": 2,
"statusLineGitCleanFg": 0,
"statusLineGitDirtyBg": 3,
"statusLineGitDirtyFg": 0,
"statusLineGitStagedBg": 64,
"statusLineGitStagedFg": 0,
"statusLineGitUntrackedBg": 39,
"statusLineGitUntrackedFg": 0,
"statusLineGitConflictBg": 1,
"statusLineGitConflictFg": 7,
"statusLinePlanModeBg": 236,
"statusLinePlanModeFg": 117,
"statusLineProfileXcshBg": "accent",
"statusLineProfileXcshFg": 231
}
}

Utilisez ce flux de travail :

  1. Démarrez le mode interactif (l’observateur est activé dès le démarrage).
  2. Ouvrez les paramètres et prévisualisez les valeurs du thème (previewTheme en direct).
  3. Pour les fichiers de thèmes personnalisés, modifiez le JSON pendant l’exécution et confirmez le rechargement automatique à la sauvegarde.
  4. Exercez les surfaces critiques :
    • rendu markdown
    • blocs d’outils (en attente/succès/erreur)
    • rendu des diffs (ajouté/supprimé/contexte)
    • lisibilité de la barre d’état
    • changements de bordure selon le niveau de réflexion
    • couleurs de bordure des modes bash/python
  5. Validez les deux préréglages de symboles si votre thème dépend de la largeur/apparence des glyphes.
  • Tous les jetons colors sont requis pour les thèmes personnalisés.
  • export et symbols sont optionnels.
  • $schema dans le JSON du thème est informatif ; la validation à l’exécution est imposée par le schéma TypeBox compilé dans le code.
  • L’échec de setTheme retombe sur dark ; l’échec de previewTheme ne remplace pas le thème actuel.
  • Les erreurs de rechargement de l’observateur de fichiers conservent le thème actuellement chargé jusqu’à un rechargement réussi ou le déclenchement du chemin de repli.