Code Block

Des blocs de code conçus pour les réponses d’IA. Coloration syntaxique qui suit le streaming, copie, téléchargement et retour à la ligne, numéros de ligne et lignes surlignées, diffs avec accepter et rejeter, et un terminal pour les commandes.

Le code d’une réponse d’IA arrive quelques caractères à la fois, souvent sous forme de diff qu’une personne doit approuver. Code Block colore pendant le streaming, suit les nouvelles lignes sauf si le lecteur a remonté, et garde la copie, le téléchargement et le retour à la ligne discrets jusqu’à ce que le code soit complet.

La coloration utilise Shiki avec les thèmes clair et sombre de GitHub, chargés à la demande par langage. Seules les nouvelles lignes sont tokenisées à mesure que le texte arrive, donc les longs fichiers restent rapides, et les deux thèmes sont rendus en même temps, si bien que changer de jeu de couleurs ne provoque jamais de flash. Les blocs de plus de 16 lignes se replient derrière « Show all ».

Passez un diff unifié et vous obtenez les numéros de ligne anciens et nouveaux, un compteur de changements et des lignes teintées ; la copie donne la nouvelle version, pas le diff. Ajoutez une revue et le lecteur peut accepter ou rejeter le changement, avec ⌘↵ et ⌘⌫ tant que le block a le focus. CodeFence branche le même block dans Streamdown, et CodeTerminal affiche des commandes avec leur sortie et leur code de sortie.

  1. Ajouter le registre Pro à components.json

    components.json
    {
      "registries": {
        "@hextaui-pro": {
          "url": "https://hextaui.com/r/pro/{name}.json",
          "headers": {
            "Authorization": "Bearer ${HEXTAUI_PRO_TOKEN}"
          }
        }
      }
    }
  2. Ajouter votre token

    Créez un token sur votre page de compte et placez-le dans .env.local sous le nom HEXTAUI_PRO_TOKEN.

  3. Ajouter le block

    pnpm dlx shadcn@latest add @hextaui-pro/code-block

Dans du Markdown avec Streamdown

Enregistrez CodeFence comme moteur de rendu. Il lit le langage et les réglages de la fence comme title="app/page.tsx", {2,4-6} et showLineNumbers, et conserve la coloration tant que la fence est encore en streaming.

"use client"

import { Streamdown } from "streamdown"

import { CodeFence, codeFenceLanguages } from "@/components/blocks/code-block/code-fence"

export function Answer({
  markdown,
  streaming,
}: {
  markdown: string
  streaming: boolean
}) {
  return (
    <Streamdown
      mode="streaming"
      isAnimating={streaming}
      plugins={{
        renderers: [{ language: codeFenceLanguages, component: CodeFence }],
      }}
    >
      {markdown}
    </Streamdown>
  )
}

Relire un changement

Montrez une modification proposée sous forme de diff et laissez le lecteur l’accepter ou la rejeter, avec ⌘↵ et ⌘⌫ tant que le block a le focus.

"use client"

import * as React from "react"

import { CodeBlock, type ReviewStatus } from "@/components/blocks/code-block/code-block"

export function ProposedEdit({
  patch,
  onAccept,
  onReject,
}: {
  patch: string
  onAccept: () => Promise<void>
  onReject: () => void
}) {
  const [status, setStatus] = React.useState<ReviewStatus>("pending")

  return (
    <CodeBlock
      code={patch}
      language="tsx"
      filename="components/search.tsx"
      diff
      review={{
        status,
        onAccept: async () => {
          await onAccept()
          setStatus("accepted")
        },
        onReject: () => {
          onReject()
          setStatus("rejected")
        },
      }}
    />
  )
}

Seul

Passez directement le code et le langage, par exemple quand un appel d’outil renvoie le contenu d’un fichier.

import { CodeBlock } from "@/components/blocks/code-block/code-block"

export function FileContents({ path, contents }: { path: string; contents: string }) {
  return (
    <CodeBlock
      code={contents}
      language={path.split(".").pop()}
      filename={path}
      lineNumbers
      highlight={[3, 4]}
    />
  )
}

Anatomie

Les parties à composer, de l’extérieur vers l’intérieur.

PartieDescription
CodeBlockLe block lui-même : en-tête, actions, code et barre de revue facultative.
CodeFenceUn moteur de rendu Streamdown qui transforme le code délimité en CodeBlock.
CodeTerminalUne commande avec sa sortie en streaming et son code de sortie.
CopyButton, DownloadButton, WrapToggleLes actions de l’en-tête, exportées pour vos propres en-têtes.
LanguageIconLa marque du langage utilisée dans l’en-tête.
PropTypePar défaut
codeLa source, ou un diff unifié lorsque diff est défini.
string–
languageIdentifiant ou alias de langage Shiki, comme tsx, py ou bash. Les identifiants inconnus s’affichent en texte brut.
string–
filenameAffiché dans l’en-tête et utilisé pour les téléchargements.
string–
streamingGarde la coloration incrémentale, suit les nouvelles lignes et désactive les actions.
booleanfalse
diffTraite le code comme un diff unifié.
booleanfalse
lineNumbersAffiche les numéros de ligne. Les diffs les affichent sauf si la valeur est false.
boolean–
startLineNuméro de la première ligne.
number1
highlightNuméros de ligne à marquer.
number[][]
defaultWrapDémarre avec les longues lignes à la ligne.
booleanfalse
collapseAfterReplie au-delà de ce nombre de lignes. 0 ne replie jamais.
number16
actionsContrôles supplémentaires dans l’en-tête, avant les contrôles intégrés.
ReactNode–
onApplyAffiche un bouton Apply qui confirme avec « Applied ».
() => unknown–
reviewAffiche la barre de revue tant que c’est en attente, et un badge une fois accepté ou rejeté.
CodeBlockReview–
PropTypePar défaut
statusLa décision actuelle.
"pending" | "accepted" | "rejected"–
onAcceptAppelé depuis Accept ou ⌘↵.
() => void–
onRejectAppelé depuis Reject ou ⌘⌫.
() => void–

CodeFence

À enregistrer avec Streamdown : plugins={{ renderers: [{ language: codeFenceLanguages, component: CodeFence }] }}.

PropTypePar défaut
codeContenu de la fence, fourni par Streamdown.
string–
languageLangage de la fence, fourni par Streamdown.
string–
metaTout ce qui suit le langage : title="…", {1,3-5}, showLineNumbers, startLine=10.
string–
isIncompletetrue tant que la fence est encore ouverte.
boolean–
PropTypePar défaut
commandLa commande exécutée.
string–
outputSortie jusqu’ici. Ajoutez-y au fil du streaming.
string""
runningAffiche un spinner et suit la nouvelle sortie.
booleanfalse
exitCodeAffiché à la fin. Toute valeur autre que 0 est marquée comme échec.
number–
titleLibellé de l’en-tête.
string"Terminal"
ToucheAction
TabParcourt les actions de l’en-tête, la zone de code et Show all.
⌘↵Accepte une revue en attente tant que le focus est dans le block.
⌘⌫Rejette une revue en attente tant que le focus est dans le block.
←→Fait défiler les longues lignes quand la zone de code a le focus.
  • La zone de code est une région focalisable nommée d’après le fichier, par exemple « app/page.tsx code », pour que les utilisateurs du clavier puissent la faire défiler.
  • Les lignes modifiées ne reposent pas sur la couleur. Les marqueurs + et − sont purement visuels, et les lecteurs d’écran entendent « added » ou « removed » avant chaque ligne modifiée à la place.
  • Copy annonce « Copied » et chaque bouton d’icône a un libellé et une infobulle.
  • Les raccourcis de revue sont ignorés pendant la saisie dans un champ, donc ils ne volent jamais de frappes.

Construit avec

Les composants HextaUI gratuits dont Code Block est constitué. Chacun s’installe séparément.

Code

9 fichiers, ajoutés à components/blocks/code-block.