> ## Documentation Index
> Fetch the complete documentation index at: https://tomee-mintlify-d7bc03a0.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Tutoriel : Créer un assistant de documentation in‑app

> Créez et intégrez un assistant de documentation in-app qui répond aux questions des utilisateurs avec des informations citées depuis votre site de documentation Mintlify.

<div id="what-you-will-build">
  ## Ce que vous allez créer
</div>

Un widget réutilisable qui intègre l’[assistant](/fr/assistant/index) directement dans votre application. Le widget propose :

* Un bouton flottant qui ouvre un panneau de conversation lorsqu’on clique dessus
* Des réponses diffusées en temps réel, basées sur les informations de votre documentation
* Un rendu des messages avec prise en charge de Markdown

Les utilisateurs peuvent utiliser le widget pour obtenir de l’aide sur votre produit sans quitter votre application.

<Frame>
  <img src="https://mintcdn.com/tomee-mintlify-d7bc03a0/zNW1lt-DocviIZki/images/assistant/assistant-embed-demo.gif?s=7621c2d0607ed4dd2247552f80673ad9" alt="Démonstration du widget Assistant qui s’ouvre, l’utilisateur tape « How do I get started? », puis l’Assistant répond." width="800" height="491" data-path="images/assistant/assistant-embed-demo.gif" />
</Frame>

<div id="prerequisites">
  ## Prérequis
</div>

* L'assistant Mintlify activé
* Votre nom de domaine, qui apparaît à la fin de l’URL de votre Dashboard. Par exemple, si l’URL de votre Dashboard est `https://dashboard.mintlify.com/org-name/domain-name`, votre nom de domaine est `domain-name`
* Une [API key de l’Assistant](https://dashboard.mintlify.com/settings/organization/api-keys)
* Node.js v18 ou version ultérieure et npm installés
* Connaissances de base de React

<div id="get-your-assistant-api-key">
  ### Récupérer votre clé d’API Assistant
</div>

1. Accédez à la page [API keys](https://dashboard.mintlify.com/settings/organization/api-keys) de votre Dashboard.
2. Cliquez sur **Create Assistant API Key**.
3. Copiez la clé d’API Assistant (commence par `mint_dsc_`) et conservez-la en lieu sûr.

<Note>
  La clé d’API Assistant est un jeton public que vous pouvez utiliser dans le code frontend. Les appels effectués avec ce jeton sont décomptés de l’allocation de messages de votre offre et peuvent entraîner des dépassements.
</Note>

<div id="set-up-the-example">
  ## Configurer l’exemple
</div>

Clonez le [référentiel d’exemple](https://github.com/mintlify/assistant-embed-example) et personnalisez-le selon vos besoins.

<Steps>
  <Step title="Cloner le référentiel">
    ```bash theme={null}
    git clone https://github.com/mintlify/assistant-embed-example.git
    cd assistant-embed-example
    ```
  </Step>

  <Step title="Choisir votre outil de développement">
    Le référentiel inclut des exemples Next.js et Vite. Choisissez l’outil que vous préférez utiliser.

    <CodeGroup>
      ```bash title="Next.js" theme={null}
      cd nextjs
      npm install
      ```

      ```bash title="Vite" theme={null}
      cd vite
      npm install
      ```
    </CodeGroup>
  </Step>

  <Step title="Configurer votre projet">
    Ouvrez `src/config.js` et mettez-le à jour avec les détails de votre projet Mintlify.

    ```js src/config.js theme={null}
    export const ASSISTANT_CONFIG = {
      domain: 'your-domain',
      docsURL: 'https://yourdocs.mintlify.app',
    };
    ```

    Remplacez:

    * `your-domain` par le domain de votre projet Mintlify, visible à la fin de l’URL de votre dashboard.
    * `https://yourdocs.mintlify.app` par l’URL réelle de votre documentation.
  </Step>

  <Step title="Ajouter votre jeton d’API">
    Créez un fichier `.env` à la racine du projet.

    ```bash .env theme={null}
    VITE_MINTLIFY_TOKEN=mint_dsc_your_token_here
    ```

    Remplacez `mint_dsc_your_token_here` par votre assistant API key.
  </Step>

  <Step title="Démarrer le serveur de développement">
    ```bash theme={null}
    npm run dev
    ```

    Ouvrez votre application dans un navigateur et cliquez le bouton **Ask** pour ouvrir le widget de l’assistant.
  </Step>
</Steps>

<div id="customization-ideas">
  ## Idées de personnalisation
</div>

<div id="source-citations">
  ### Citations de sources
</div>

Extraire et afficher les sources à partir des réponses de l’Assistant :

```jsx theme={null}
const extractSources = (parts) => {
  return parts
    ?.filter(p => p.type === 'tool-invocation' && p.toolInvocation?.toolName === 'search')
    .flatMap(p => p.toolInvocation?.result || [])
    .map(source => ({
      url: source.url || source.path,
      title: source.metadata?.title || source.path,
    })) || [];
};

// In your message rendering:
{messages.map((message) => {
  const sources = message.role === 'assistant' ? extractSources(message.parts) : [];
  return (
    <div key={message.id}>
      {/* contenu du message */}
      {sources.length > 0 && (
        <div className="mt-2 text-xs">
          <p className="font-semibold">Sources :</p>
          {sources.map((s, i) => (
            <a key={i} href={s.url} target="_blank" rel="noopener noreferrer" className="text-blue-600">
              {s.title}
            </a>
          ))}
        </div>
      )}
    </div>
  );
})}
```

<div id="track-conversation-thread-ids">
  ### Suivre les fils de conversation
</div>

Stockez les identifiants de fil et les clés de fil pour conserver l'historique des conversations entre les sessions.

Lorsqu'un utilisateur crée un nouveau fil de conversation, le serveur renvoie deux valeurs dans les en-têtes de la réponse :

* `X-Thread-Id` : L'identifiant du fil
* `X-Thread-Key` : Une clé secrète pour le fil (renvoyée une seule fois, lors de la création du fil)

Vous devez capturer et persister les deux valeurs dès la première réponse. Pour chaque message suivant, incluez à la fois `threadId` et `threadKey` dans le corps de la requête. Si vous envoyez un `threadId` sans le `threadKey` correspondant, le serveur renvoie une erreur `404`.

<Warning>
  Vous devez stocker l'en-tête `X-Thread-Key` immédiatement. Le serveur ne le renvoie que lors de la création d'un nouveau fil. Vous ne pouvez pas le récupérer ultérieurement.
</Warning>

```jsx theme={null}
import { useState, useEffect } from 'react';

export function AssistantWidget({ domain, docsURL }) {
  const [threadId, setThreadId] = useState(null);
  const [threadKey, setThreadKey] = useState(null);

  useEffect(() => {
    // Récupérer l'ID et la clé de fil enregistrés depuis localStorage
    const savedId = localStorage.getItem('assistant-thread-id');
    const savedKey = localStorage.getItem('assistant-thread-key');
    if (savedId && savedKey) {
      setThreadId(savedId);
      setThreadKey(savedKey);
    }
  }, []);

  const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
    api: `https://api.mintlify.com/discovery/v1/assistant/${domain}/message`,
    headers: {
      'Authorization': `Bearer ${import.meta.env.VITE_MINTLIFY_TOKEN}`,
    },
    body: {
      fp: 'anonymous',
      retrievalPageSize: 5,
      ...(threadId && { threadId }),
      ...(threadKey && { threadKey }),
    },
    streamProtocol: 'data',
    sendExtraMessageFields: true,
    fetch: async (url, options) => {
      const response = await fetch(url, options);
      const newThreadId = response.headers.get('x-thread-id');
      const newThreadKey = response.headers.get('x-thread-key');
      if (newThreadId) {
        setThreadId(newThreadId);
        localStorage.setItem('assistant-thread-id', newThreadId);
      }
      if (newThreadKey) {
        setThreadKey(newThreadKey);
        localStorage.setItem('assistant-thread-key', newThreadKey);
      }
      return response;
    },
  });

  // ... reste du composant
}
```

<div id="add-keyboard-shortcuts">
  ### Ajouter des raccourcis clavier
</div>

Permettez aux utilisateurs d’ouvrir le widget et d’envoyer des messages à l’aide de raccourcis clavier :

```jsx theme={null}
useEffect(() => {
  const handleKeyDown = (e) => {
    // Cmd/Ctrl + Shift + I pour afficher/masquer le widget
    if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key === 'I') {
      e.preventDefault();
      setIsOpen((prev) => !prev);
    }

    // Entrée (lorsque le widget a le focus) pour envoyer
    if (e.key === 'Enter' && !e.shiftKey && document.activeElement.id === 'assistant-input') {
      e.preventDefault();
      handleSubmit();
    }
  };

  window.addEventListener('keydown', handleKeyDown);
  return () => window.removeEventListener('keydown', handleKeyDown);
}, [handleSubmit]);
```
