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

# Widget Mintlify

> Installez et configurez le widget Mintlify pour intégrer l'assistant IA entraîné sur votre contenu dans n'importe quel site web ou application web.

export const AssistantWidgetPlayground = ({children, CodeBlockComponent, CustomizeIconComponent, ThemeIconComponent}) => {
  const EXAMPLE_WIDGET_ID = "YOUR_WIDGET_ID";
  const EMBED_URL = "https://cdn.jsdelivr.net/npm/@mintlify/assistant-widget@0.0/dist/browser/embed.js";
  const PREVIEW_READY_MESSAGE = "mintlify-assistant-playground:ready";
  const PREVIEW_UPDATE_MESSAGE = "mintlify-assistant-playground:update";
  const PREVIEW_STATE_MESSAGE = "mintlify-assistant-playground:state";
  const SUPPORT_EMAIL = "hi@mintlify.com";
  const STARTER_QUESTIONS = ["How do I get started with Mintlify?", "How do I customize my docs?", "How do I deploy my docs?"];
  const VARIANT_OPTIONS = [{
    value: "widget",
    label: "Widget"
  }, {
    value: "modal",
    label: "Modal"
  }, {
    value: "panel",
    label: "Panel"
  }];
  const RADIUS_OPTIONS = [{
    value: 0,
    label: "None",
    detail: "0px"
  }, {
    value: 4,
    label: "Extra small",
    detail: "4px"
  }, {
    value: 8,
    label: "Small",
    detail: "8px"
  }, {
    value: 12,
    label: "Medium",
    detail: "12px"
  }, {
    value: 16,
    label: "Large",
    detail: "16px"
  }, {
    value: 20,
    label: "Extra large",
    detail: "20px"
  }, {
    value: 24,
    label: "2X large",
    detail: "24px"
  }];
  const SIDE_OPTIONS = [{
    value: "bottom",
    label: "Top"
  }, {
    value: "top",
    label: "Bottom"
  }, {
    value: "right",
    label: "Left"
  }, {
    value: "left",
    label: "Right"
  }, {
    value: "inline-end",
    label: "Inline start"
  }, {
    value: "inline-start",
    label: "Inline end"
  }];
  const ALIGN_OPTIONS = [{
    value: "start",
    label: "Start"
  }, {
    value: "center",
    label: "Center"
  }, {
    value: "end",
    label: "End"
  }];
  const INSTALL_OPTIONS = [{
    value: "html",
    label: "HTML"
  }, {
    value: "next",
    label: "Next.js"
  }];
  const [installTarget, setInstallTarget] = useState("html");
  const [variant, setVariant] = useState("widget");
  const [previewTheme, setPreviewTheme] = useState(null);
  const [accent, setAccent] = useState("#166E3F");
  const [accentDraft, setAccentDraft] = useState("#166E3F");
  const [accentKeyboardFocus, setAccentKeyboardFocus] = useState(false);
  const [radius, setRadius] = useState(16);
  const [side, setSide] = useState("bottom");
  const [align, setAlign] = useState("end");
  const [trackEvents, setTrackEvents] = useState(false);
  const [reportErrors, setReportErrors] = useState(false);
  const [customizeOpen, setCustomizeOpen] = useState(false);
  const [openSelect, setOpenSelect] = useState(null);
  const [activeSelectOptionIndex, setActiveSelectOptionIndex] = useState(0);
  const [previewHostReady, setPreviewHostReady] = useState(false);
  const [previewUrl, setPreviewUrl] = useState(null);
  const [previewStatus, setPreviewStatus] = useState("loading");
  const accentInputRef = useRef(null);
  const accentPointerFocusRef = useRef(false);
  const previewRef = useRef(null);
  const previewHostRef = useRef(null);
  useEffect(() => {
    const pageMatch = window.location.pathname.replace(/\/$/, "").match(/^(.*?)(\/[a-z]{2}(?:-[A-Za-z]{2,4})?)?\/assistant\/widget$/);
    const basePath = pageMatch?.[1] ?? "";
    const mode = document.documentElement.classList.contains("dark") ? "dark" : "light";
    setPreviewUrl(`${basePath}/assistant/widget-preview?mode=${mode}`);
  }, []);
  useEffect(() => {
    const removeRootWidget = () => {
      const rootWidget = document.querySelector("body > mintlify-assistant");
      if (!rootWidget) return false;
      const destroyPromise = window.MintlifyAssistant?.destroy();
      void destroyPromise?.catch(() => {});
      rootWidget.remove();
      return true;
    };
    const rootWidgetObserver = new MutationObserver(removeRootWidget);
    removeRootWidget();
    rootWidgetObserver.observe(document.body, {
      childList: true
    });
    return () => rootWidgetObserver.disconnect();
  }, []);
  useEffect(() => {
    const host = previewHostRef.current;
    if (!host) return undefined;
    const markReady = height => {
      if (height > 0) setPreviewHostReady(true);
    };
    markReady(host.getBoundingClientRect().height);
    if (typeof ResizeObserver === "undefined") {
      setPreviewHostReady(true);
      return undefined;
    }
    const observer = new ResizeObserver(entries => {
      markReady(entries[0]?.contentRect.height ?? 0);
    });
    observer.observe(host);
    return () => observer.disconnect();
  }, []);
  useEffect(() => {
    if (!openSelect) return undefined;
    const closeOnPointerDown = event => {
      if (event.target instanceof Element && event.target.closest(`[data-assistant-select="${openSelect}"]`)) {
        return;
      }
      setOpenSelect(null);
    };
    const closeOnEscape = event => {
      if (event.key === "Escape") setOpenSelect(null);
    };
    document.addEventListener("pointerdown", closeOnPointerDown);
    document.addEventListener("keydown", closeOnEscape);
    return () => {
      document.removeEventListener("pointerdown", closeOnPointerDown);
      document.removeEventListener("keydown", closeOnEscape);
    };
  }, [openSelect]);
  useEffect(() => {
    const accentInput = accentInputRef.current;
    if (!accentInput) return undefined;
    const commitAccent = () => {
      const nextAccent = accentInput.value.toUpperCase();
      setAccentDraft(nextAccent);
      setAccent(nextAccent);
    };
    accentInput.addEventListener("change", commitAccent);
    return () => accentInput.removeEventListener("change", commitAccent);
  }, []);
  const renderSelectField = ({id, label, onChange, options, value}) => {
    const isOpen = openSelect === id;
    const selectedIndex = options.findIndex(option => option.value === value);
    const selectedOption = options[selectedIndex] ?? options[0];
    const openMenu = (initialIndex = selectedIndex) => {
      setActiveSelectOptionIndex(Math.max(initialIndex, 0));
      setOpenSelect(id);
    };
    const closeMenu = () => setOpenSelect(null);
    const selectByIndex = index => {
      const option = options[index];
      if (!option) return;
      onChange(option.value);
      closeMenu();
    };
    const handleKeyDown = event => {
      if (event.key === "ArrowDown" || event.key === "ArrowUp") {
        event.preventDefault();
        if (!isOpen) {
          openMenu();
          return;
        }
        const direction = event.key === "ArrowDown" ? 1 : -1;
        setActiveSelectOptionIndex(currentIndex => (currentIndex + direction + options.length) % options.length);
        return;
      }
      if (event.key === "Home" || event.key === "End") {
        event.preventDefault();
        if (!isOpen) openMenu();
        setActiveSelectOptionIndex(event.key === "Home" ? 0 : options.length - 1);
        return;
      }
      if (event.key === "Enter" || event.key === " ") {
        event.preventDefault();
        if (isOpen) {
          selectByIndex(activeSelectOptionIndex);
        } else {
          openMenu();
        }
        return;
      }
      if (event.key === "Escape" && isOpen) {
        event.preventDefault();
        event.stopPropagation();
        closeMenu();
      }
    };
    return <div className="assistant-playground-field">
        <span id={`assistant-playground-${id}-label`} className="assistant-playground-field__label">
          {label}
        </span>
        <div className="assistant-playground-select" data-assistant-select={id} data-open={isOpen ? "true" : "false"} onBlur={event => {
      if (!(event.relatedTarget instanceof Node) || !event.currentTarget.contains(event.relatedTarget)) {
        setOpenSelect(null);
      }
    }}>
          <button type="button" className="assistant-playground-field__control" aria-haspopup="listbox" aria-expanded={isOpen} aria-controls={`assistant-playground-${id}-options`} aria-activedescendant={isOpen ? `assistant-playground-${id}-option-${activeSelectOptionIndex}` : undefined} aria-labelledby={`assistant-playground-${id}-label assistant-playground-${id}-value`} onClick={() => {
      if (isOpen) {
        closeMenu();
      } else {
        openMenu();
      }
    }} onKeyDown={handleKeyDown}>
            <span id={`assistant-playground-${id}-value`}>
              {selectedOption?.label}
            </span>
            <span className="assistant-playground-select__end">
              {selectedOption?.detail ? <span className="assistant-playground-select__detail">
                  {selectedOption.detail}
                </span> : null}
              <svg aria-hidden="true" className="assistant-playground-select__chevron" viewBox="0 0 16 16">
                <path d="M4 6L8 10L12 6" fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5" />
              </svg>
            </span>
          </button>
          {isOpen ? <div id={`assistant-playground-${id}-options`} className="assistant-playground-select__options" role="listbox" aria-labelledby={`assistant-playground-${id}-label`}>
              {options.map((option, index) => <button key={option.value} id={`assistant-playground-${id}-option-${index}`} type="button" role="option" tabIndex={-1} aria-selected={option.value === value} data-active={activeSelectOptionIndex === index ? "true" : "false"} data-selected={option.value === value ? "true" : "false"} onPointerDown={event => event.preventDefault()} onPointerMove={() => setActiveSelectOptionIndex(index)} onClick={() => {
      selectByIndex(index);
    }}>
                  <span>{option.label}</span>
                  {option.detail ? <span className="assistant-playground-select__detail">
                      {option.detail}
                    </span> : null}
                </button>)}
            </div> : null}
        </div>
      </div>;
  };
  const renderToggleRow = ({checked, description, label, onChange}) => <label className="assistant-playground-hook">
      <span className="assistant-playground-hook__copy">
        <span className="assistant-playground-hook__label">{label}</span>
        <span className="assistant-playground-hook__description">{description}</span>
      </span>
      <span className="assistant-playground-switch" data-checked={checked ? "true" : "false"}>
        <input type="checkbox" role="switch" checked={checked} onChange={event => onChange(event.target.checked)} className="assistant-playground-switch__input" />
        <span aria-hidden="true" className="assistant-playground-switch__knob" />
      </span>
    </label>;
  const renderDivider = () => <div role="separator" aria-orientation="horizontal" className="assistant-playground-customizer__divider">
      <svg aria-hidden="true" focusable="false" width="100%" height="1" preserveAspectRatio="none" viewBox="0 0 100 1">
        <line x1="0" y1="0.5" x2="100" y2="0.5" stroke="currentColor" strokeWidth="1" strokeDasharray="5 5" strokeLinecap="butt" vectorEffect="non-scaling-stroke" />
      </svg>
    </div>;
  const appearance = useMemo(() => ({
    variant,
    accent,
    radius: `${radius}px`,
    side,
    align
  }), [accent, align, radius, side, variant]);
  const togglePreviewTheme = () => {
    setPreviewTheme(currentTheme => {
      const isDark = currentTheme === "dark" || currentTheme === null && document.documentElement.classList.contains("dark");
      return isDark ? "light" : "dark";
    });
  };
  const toggleCustomizer = () => {
    setCustomizeOpen(isOpen => !isOpen);
    setOpenSelect(null);
  };
  const updatePreview = useCallback(() => {
    const previewWindow = previewRef.current?.contentWindow;
    if (!previewWindow) return;
    const liveTheme = previewTheme ?? (document.documentElement.classList.contains("dark") ? "dark" : "light");
    previewWindow.postMessage({
      type: PREVIEW_UPDATE_MESSAGE,
      trackEvents,
      reportErrors,
      appearance: {
        ...appearance,
        theme: liveTheme
      }
    }, window.location.origin);
  }, [appearance, previewTheme, reportErrors, trackEvents]);
  useEffect(() => {
    const handlePreviewMessage = event => {
      if (event.source !== previewRef.current?.contentWindow) return;
      if (event.data?.type === PREVIEW_READY_MESSAGE) {
        updatePreview();
        return;
      }
      if (event.data?.type === PREVIEW_STATE_MESSAGE) {
        setPreviewStatus(event.data.state === "error" ? "error" : "ready");
      }
    };
    const themeObserver = new MutationObserver(updatePreview);
    window.addEventListener("message", handlePreviewMessage);
    themeObserver.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ["class"]
    });
    updatePreview();
    return () => {
      window.removeEventListener("message", handlePreviewMessage);
      themeObserver.disconnect();
    };
  }, [updatePreview]);
  useEffect(() => {
    if (!previewHostReady || !previewUrl || previewStatus !== "loading") {
      return undefined;
    }
    const timeout = window.setTimeout(() => {
      setPreviewStatus(status => status === "loading" ? "error" : status);
    }, 20000);
    return () => window.clearTimeout(timeout);
  }, [previewHostReady, previewStatus, previewUrl]);
  const configLines = ["{", `  id: '${EXAMPLE_WIDGET_ID}',`, `  supportEmail: '${SUPPORT_EMAIL}',`, "  starterQuestions: [", ...STARTER_QUESTIONS.map(question => `    '${question}',`), "  ],", "  appearance: {", `    variant: '${variant}',`, "    theme: 'system',", `    accent: '${accent}',`, `    radius: '${radius}px',`, `    side: '${side}',`, `    align: '${align}',`, "  },"];
  if (trackEvents || reportErrors) {
    configLines.push("  hooks: {");
    if (trackEvents) {
      configLines.push("    event(event) {", "      console.log('Assistant event', event);", "    },");
    }
    if (reportErrors) {
      configLines.push("    error(error) {", "      console.error('Assistant error', error.code, error);", "    },");
    }
    configLines.push("  },");
  }
  configLines.push("}");
  const configCode = configLines.join("\n");
  const indentedConfig = configCode.split("\n").join("\n  ");
  const htmlCode = `<script
  type="module"
  src="${EMBED_URL}"
></script>
<script type="module">
  await window.MintlifyAssistant.init(${indentedConfig});
</script>`;
  const nextCode = `'use client';

import Script from 'next/script';

const ASSISTANT_CONFIG = ${configCode};

export const AssistantWidget = () => (
  <Script
    type="module"
    src="${EMBED_URL}"
    onReady={() => {
      void window.MintlifyAssistant.init(ASSISTANT_CONFIG);
    }}
  />
);`;
  const installCode = installTarget === "html" ? htmlCode : nextCode;
  return <div data-assistant-playground-layout="">
      <section className="assistant-playground-frame not-prose" data-customize-open={customizeOpen ? "true" : "false"} aria-label="Assistant widget playground">
        <div className="assistant-playground-toolbar">
          <button type="button" className="assistant-playground-toolbar__button assistant-playground-toolbar__button--labeled" aria-controls="assistant-playground-customizer" aria-expanded={customizeOpen} onClick={toggleCustomizer}>
            {CustomizeIconComponent ? <CustomizeIconComponent /> : null}
            <span>Customize</span>
          </button>
          <button type="button" className="assistant-playground-toolbar__button assistant-playground-toolbar__button--icon" aria-label="Toggle preview theme" onClick={togglePreviewTheme}>
            {ThemeIconComponent ? <ThemeIconComponent /> : null}
          </button>
        </div>

        <div id="assistant-playground-customizer" className="assistant-playground-customizer" role="dialog" aria-label="Customize assistant widget" hidden={!customizeOpen}>
          <div className="assistant-playground-customizer__section">
            <div className="assistant-playground-customizer__heading">
              Component
            </div>
            {renderSelectField({
    id: "variant",
    label: "Variant",
    value: variant,
    options: VARIANT_OPTIONS,
    onChange: setVariant
  })}
            {renderSelectField({
    id: "radius",
    label: "Corner radius",
    value: radius,
    options: RADIUS_OPTIONS,
    onChange: setRadius
  })}
            <label className="assistant-playground-field">
              <span className="assistant-playground-field__label">Accent</span>
              <span className="assistant-playground-accent" data-keyboard-focus={accentKeyboardFocus ? "true" : "false"}>
                <input ref={accentInputRef} type="color" value={accentDraft} onInput={event => setAccentDraft(event.currentTarget.value.toUpperCase())} onPointerDown={() => {
    accentPointerFocusRef.current = true;
    setAccentKeyboardFocus(false);
  }} onFocus={() => {
    setAccentKeyboardFocus(!accentPointerFocusRef.current);
    accentPointerFocusRef.current = false;
  }} onBlur={event => {
    setAccentKeyboardFocus(false);
    const nextAccent = event.currentTarget.value.toUpperCase();
    setAccentDraft(nextAccent);
    setAccent(nextAccent);
  }} onKeyDown={event => {
    setAccentKeyboardFocus(true);
    if (event.key === "Enter") {
      const nextAccent = event.currentTarget.value.toUpperCase();
      setAccentDraft(nextAccent);
      setAccent(nextAccent);
    }
  }} aria-label="Accent color" className="assistant-playground-accent__input" />
                <span aria-hidden="true" className="assistant-playground-accent__swatch" style={{
    backgroundColor: accentDraft
  }} />
                <span>{accentDraft}</span>
              </span>
            </label>
          </div>

          {renderDivider()}

          <div className="assistant-playground-customizer__section">
            <div className="assistant-playground-customizer__heading">Trigger</div>
            {renderSelectField({
    id: "side",
    label: "Alignment",
    value: side,
    options: SIDE_OPTIONS,
    onChange: setSide
  })}
            {renderSelectField({
    id: "align",
    label: "Placement",
    value: align,
    options: ALIGN_OPTIONS,
    onChange: setAlign
  })}
          </div>

          {renderDivider()}

          <div className="assistant-playground-customizer__section">
            <div className="assistant-playground-customizer__heading">Hooks</div>
            {renderToggleRow({
    label: "Lifecycle events",
    description: "Observe open, close, ask, update, and navigation events.",
    checked: trackEvents,
    onChange: setTrackEvents
  })}
            {renderToggleRow({
    label: "Structured errors",
    description: "Receive stable error codes and retry metadata.",
    checked: reportErrors,
    onChange: setReportErrors
  })}
          </div>
        </div>

        <div ref={previewHostRef} className="assistant-playground-preview" data-assistant-preview="" data-assistant-preview-card="">
          {previewHostReady && previewUrl ? <iframe ref={previewRef} title="Live Assistant Widget preview" src={previewUrl} onLoad={updatePreview} scrolling="no" /> : null}
          {previewStatus === "loading" ? <div aria-live="polite" role="status" className="assistant-playground-preview__status">
              <span aria-hidden="true" className="assistant-playground-preview__spinner" />
              <span>Loading assistant preview...</span>
            </div> : null}
        </div>
      </section>

      <div className="assistant-playground-code not-prose" data-assistant-code="">
        <div className="assistant-playground-code__header">
          <div>
            <div className="assistant-playground-code__title">Install</div>
            <div className="assistant-playground-code__description">
              Copy the generated setup for your stack.
            </div>
          </div>
          <div role="group" aria-label="Installation target" className="assistant-playground-code__tabs">
            {INSTALL_OPTIONS.map(option => <button key={option.value} type="button" data-active={installTarget === option.value ? "true" : "false"} aria-pressed={installTarget === option.value} onClick={() => setInstallTarget(option.value)}>
                {option.label}
              </button>)}
          </div>
        </div>
        <CodeBlockComponent language="jsx" filename={installTarget === "html" ? "index.html" : "assistant-widget.jsx"} wrap>
          {installCode}
        </CodeBlockComponent>
      </div>

      {children ? <div className="assistant-playground-children">{children}</div> : null}
    </div>;
};

export const WidgetCodeBlock = ({children, ...props}) => <CodeBlock {...props}>{children}</CodeBlock>;

export const WidgetCustomizeIcon = () => <Icon icon="scan-text" size={16} />;

export const WidgetThemeIcon = () => <span aria-hidden="true" className="assistant-playground-theme-icon" />;

L'[assistant](/fr/assistant) répond aux questions sur votre site Mintlify. Pour intégrer la même capacité sur un autre site ou application web, utilisez le widget. Grâce au widget, vous pouvez donner à vos utilisateurs l'accès à un chat IA entraîné sur votre contenu dans le tableau de bord de votre produit, votre site marketing, votre portail d'assistance ou ailleurs.

Ajoutez le widget à n'importe quel site web ou application web à l'aide d'un script hébergé. Le widget possède son propre déclencheur et s'affiche dans un Shadow DOM fermé, ce qui empêche les styles de votre application d'affecter le widget.

La seule option de navigateur requise est l'ID public du widget. Gérez l'état d'activation, les origines autorisées, les pièces jointes et la protection contre les bots dans votre tableau de bord. Définissez les questions d'introduction spécifiques à l'intégration et une adresse e-mail d'assistance dans la configuration du navigateur.

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

* Un [plan Pro ou Enterprise](https://mintlify.com/pricing?ref=assistant). Le widget utilise les mêmes crédits que l'assistant.

<div id="enable-the-widget">
  ## Activer le widget
</div>

1. Accédez à la page [Widget](https://app.mintlify.com/settings/deployment/widget) de votre déploiement.
2. Activez le widget.
3. Ajoutez les origines autorisées où vous intégrez le widget.
4. Copiez l'ID du widget.

<div id="install-and-configure">
  ## Installer et configurer
</div>

Utilisez le playground pour configurer la présentation, les options visuelles et les hooks d'observation de votre widget. Le bloc de code d'installation se met à jour à mesure que vous modifiez chaque option.

<Info>
  Remplacez `YOUR_WIDGET_ID` dans le code généré par l'ID du widget de la page [Widget](https://app.mintlify.com/settings/deployment/widget) de votre tableau de bord.
</Info>

Après avoir ajouté le code généré à votre site, rechargez la page. Vérifiez que le déclencheur apparaît, puis cliquez dessus et envoyez une question de test pour confirmer que le widget est connecté.

<AssistantWidgetPlayground CodeBlockComponent={WidgetCodeBlock} CustomizeIconComponent={WidgetCustomizeIcon} ThemeIconComponent={WidgetThemeIcon}>
  <Warning>
    Les scripts de module sont différés et exécutés dans l'ordre du document. Gardez le chargeur hébergé avant le bloc d'initialisation lorsque vous installez le widget en HTML, sinon le widget ne parvient pas à se monter.
  </Warning>

  <div id="open-on-initialization">
    ## Ouvrir lors de l'initialisation
  </div>

  Définissez `defaultOpen` sur `true` pour ouvrir le widget immédiatement après son premier montage :

  ```js theme={null}
  await window.MintlifyAssistant.init({
    id: "YOUR_WIDGET_ID",
    defaultOpen: true,
  });
  ```

  `defaultOpen` est par défaut à `false` et ne s'applique qu'à la première initialisation. Appeler à nouveau `init()` avec le même ID de widget et le même endpoint API ne rouvre pas un widget qu'un visiteur a fermé. Utilisez `open()` et `close()` pour le contrôler après l'initialisation.

  <div id="use-a-custom-trigger">
    ## Utiliser un déclencheur personnalisé
  </div>

  Attendez `init()` avant d'appeler d'autres méthodes. Conservez le déclencheur intégré ou ouvrez la présentation configurée depuis n'importe quel bouton de votre application.

  ```js theme={null}
  await window.MintlifyAssistant.init({
    id: "YOUR_WIDGET_ID",
    supportEmail: "hi@mintlify.com",
    starterQuestions: [
      "How do I get started with Mintlify?",
      "How do I customize my docs?",
      "How do I deploy my docs?",
    ],
  });

  document.querySelector("#help-button").addEventListener("click", () => {
    void window.MintlifyAssistant.open({
      source: "help-button",
      focus: true,
    });
  });
  ```

  Pour ouvrir le widget et envoyer immédiatement une question, appelez `ask()` :

  ```js theme={null}
  await window.MintlifyAssistant.ask("How do I authenticate?", {
    source: "authentication-guide",
    open: true,
    focus: true,
  });
  ```

  Les métadonnées d'événement et les requêtes incluent la valeur `source`, ce qui vous permet de distinguer les interactions intégrées de vos points d'entrée personnalisés.

  <div id="update-a-mounted-widget">
    ## Mettre à jour un widget monté
  </div>

  Utilisez `update()` pour modifier l'apparence, les libellés, l'e-mail d'assistance, les questions d'introduction ou les hooks sans effacer la conversation en cours. Seuls les champs fournis sont modifiés.

  ```js theme={null}
  await window.MintlifyAssistant.update({
    appearance: {
      theme: "dark",
      accent: "#7c3aed",
    },
    labels: {
      title: "Docs copilot",
      trigger: "Ask docs",
    },
    supportEmail: "support@example.com",
    starterQuestions: [
      "How do I get started?",
      "How do I manage my account?",
    ],
  });
  ```

  Passez `null` pour restaurer un champ ou un groupe à sa valeur par défaut, supprimer l'e-mail d'assistance ou restaurer une liste vide de questions d'introduction :

  ```js theme={null}
  await window.MintlifyAssistant.update({
    appearance: {
      accent: null,
    },
    supportEmail: null,
    starterQuestions: null,
    hooks: null,
  });
  ```

  La modification de `identity` démarre une nouvelle conversation. La modification de l'ID du widget ou de l'endpoint API nécessite d'appeler `destroy()` avant un nouveau `init()`.

  Vous pouvez fournir `supportEmail` et `starterQuestions` lors de l'initialisation et les modifier ultérieurement avec `update()`. Ces valeurs s'appliquent à l'intégration en cours et ne sont pas héritées de votre tableau de bord Mintlify.

  <div id="scope-retrieval-by-language-or-version">
    ## Restreindre la recherche par langue ou version
  </div>

  Utilisez `filter` pour restreindre ce que l'assistant récupère lorsque votre documentation est organisée par langue ou par [versions](/fr/organize/navigation#versions). Omettez un champ pour rechercher dans toutes ses valeurs.

  ```js theme={null}
  await window.MintlifyAssistant.init({
    id: "YOUR_WIDGET_ID",
    filter: {
      language: "en",
      version: "v2",
    },
  });
  ```

  `language` doit être un [code de langue pris en charge](/fr/organize/settings-reference#navigation-global-languages), tel que `en`, `es`, `fr` ou `zh-Hans`. `version` correspond au nom de la version configurée dans votre tableau de bord.

  Modifiez les filtres au moment de l'exécution avec `update()` lorsque le visiteur change de langue ou de version dans votre application :

  ```js theme={null}
  await window.MintlifyAssistant.update({
    filter: {
      language: "fr",
      version: null,
    },
  });
  ```

  Passez `null` sur un champ pour effacer ce filtre, ou `filter: null` pour effacer les deux.

  <div id="configuration-reference">
    ## Référence de configuration
  </div>

  <div id="assistantconfig">
    ### `AssistantConfig`
  </div>

  Passez cet objet à `init()`.

  | Option             | Type                                          | Description                                                                                      |
  | ------------------ | --------------------------------------------- | ------------------------------------------------------------------------------------------------ |
  | `id`               | string                                        | ID public du widget provenant du tableau de bord Mintlify.                                       |
  | `endpoint`         | string                                        | Remplace l'endpoint API du widget hébergé.                                                       |
  | `identity`         | string                                        | Jeton d'identité signé de l'utilisateur final. Omettez-le pour les visiteurs anonymes.           |
  | `nonce`            | string                                        | Nonce CSP copié dans les ressources créées par le widget.                                        |
  | `defaultOpen`      | boolean                                       | Ouvre le widget lors de sa première initialisation. La valeur par défaut est `false`.            |
  | `appearance`       | [`AssistantAppearance`](#assistantappearance) | Substitutions visuelles et de présentation.                                                      |
  | `labels`           | [`AssistantLabels`](#assistantlabels)         | Substitutions du texte visible par le client.                                                    |
  | `supportEmail`     | string                                        | Définit l'adresse d'assistance affichée dans la barre d'outils du widget pour cette intégration. |
  | `starterQuestions` | string\[]                                     | Définit jusqu'à **trois** invites d'état vide pour cette intégration.                            |
  | `filter`           | [`AssistantFilter`](#assistantfilter)         | Restreint la recherche à une langue et à une version de la documentation.                        |
  | `hooks`            | [`AssistantHooks`](#assistanthooks)           | Observateurs d'événements et d'erreurs.                                                          |

  <div id="assistantappearance">
    ### `AssistantAppearance`
  </div>

  | Option                     | Values                                                         | Description                                                                                                          |
  | -------------------------- | -------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
  | `variant`                  | `widget`, `modal`, `panel`                                     | Contrôle si l'assistant s'ouvre comme un popover ancré, une boîte de dialogue centrée ou un panneau latéral réactif. |
  | `theme`                    | `light`, `dark`, `system`                                      | Définit le schéma de couleurs du widget. La valeur par défaut est `system`.                                          |
  | `accent`                   | CSS color                                                      | Définit la couleur des contrôles principaux.                                                                         |
  | `radius`                   | CSS border radius                                              | Définit le rayon du panneau, par exemple `18px`.                                                                     |
  | `font`                     | CSS font family                                                | Utilise une police déjà chargée par votre application. Par défaut, Inter est intégrée.                               |
  | `side`                     | `top`, `bottom`, `left`, `right`, `inline-start`, `inline-end` | Positionne le déclencheur intégré sur un bord de l'écran.                                                            |
  | `align`                    | `start`, `center`, `end`                                       | Aligne le déclencheur le long du bord sélectionné.                                                                   |
  | `dismissOnInteractOutside` | boolean                                                        | Contrôle si les interactions du pointeur ou du focus à l'extérieur ferment l'assistant.                              |
  | `logo`                     | URL or `{ light, dark }`                                       | Remplace la marque Mintlify par défaut.                                                                              |
  | `zIndex`                   | number                                                         | Modifie l'ordre d'empilement de l'hôte du widget.                                                                    |

  Les substitutions CSS arbitraires et les palettes neutres ne sont pas prises en charge. Le Shadow DOM fermé protège à la fois votre application et le widget des régressions de style entre sites.

  <div id="assistantfilter">
    ### `AssistantFilter`
  </div>

  | Option     | Type             | Description                                                                                                                                                                              |
  | ---------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | `language` | string or `null` | Restreint la recherche à un [code de langue pris en charge](/fr/organize/settings-reference#navigation-global-languages), tel que `en`. Omettez pour rechercher dans toutes les langues. |
  | `version`  | string or `null` | Restreint la recherche à une version de la documentation, telle que `v2`. Omettez pour rechercher dans toutes les versions.                                                              |

  <div id="assistantlabels">
    ### `AssistantLabels`
  </div>

  | Option        | Values                     | Description                                                                                      |
  | ------------- | -------------------------- | ------------------------------------------------------------------------------------------------ |
  | `title`       | string or `null`           | Définit l'en-tête du panneau. La valeur par défaut est `Assistant`.                              |
  | `trigger`     | string or `null`           | Définit le texte compact du widget et du déclencheur du panneau.                                 |
  | `placeholder` | string or `null`           | Définit le placeholder du compositeur et du déclencheur modal.                                   |
  | `disclaimer`  | string, `false`, or `null` | Définit l'avis d'état vide. Passez `false` pour le masquer.                                      |
  | `suggestions` | string or `null`           | Définit le titre au-dessus des questions d'introduction. La valeur par défaut est `Suggestions`. |

  <div id="assistanthooks">
    ### `AssistantHooks`
  </div>

  ```js theme={null}
  hooks: {
    event(event) {
      console.log(event.type, event.actor, event.source);
    },
    error(error) {
      console.error(error.code, error.retryable, error.status);
    },
  }
  ```

  Le hook `event` reçoit les métadonnées de cycle de vie et d'interaction pour `init`, `open`, `close`, `ask`, `update`, `reset`, `navigate` et `destroy`. Les événements n'incluent pas le texte de la question, l'identité, la session ou les jetons CAPTCHA.

  Le hook `error` reçoit un `code` stable, un booléen `retryable` et un `status` HTTP facultatif. Les exceptions levées par l'un ou l'autre des hooks n'interrompent pas le widget.

  <div id="assistantopenoptions">
    ### `AssistantOpenOptions`
  </div>

  Passez cet objet facultatif à `open()`.

  | Option   | Type    | Description                                                                       |
  | -------- | ------- | --------------------------------------------------------------------------------- |
  | `source` | string  | Attribution définie par le client incluse dans les événements et les requêtes.    |
  | `focus`  | boolean | Met le focus sur le compositeur après ouverture. La valeur par défaut est `true`. |

  <div id="assistantaskoptions">
    ### `AssistantAskOptions`
  </div>

  Passez cet objet facultatif après la chaîne de question dans `ask()`.

  | Option   | Type    | Description                                                                           |
  | -------- | ------- | ------------------------------------------------------------------------------------- |
  | `source` | string  | Attribution définie par le client incluse dans les événements et les requêtes.        |
  | `open`   | boolean | Ouvre le panneau avant d'envoyer. La valeur par défaut est `true`.                    |
  | `focus`  | boolean | Met le focus sur le compositeur lors de l'ouverture. La valeur par défaut est `true`. |

  <div id="assistantupdate">
    ### `AssistantUpdate`
  </div>

  Passez cet objet à `update()`. Chaque champ est facultatif, et `null` restaure sa valeur par défaut.

  | Option             | Type                                                    | Description                                                                                            |
  | ------------------ | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
  | `identity`         | string or `null`                                        | Change l'identité signée et démarre une nouvelle conversation.                                         |
  | `appearance`       | [`AssistantAppearance`](#assistantappearance) or `null` | Applique un patch profond aux paramètres d'apparence.                                                  |
  | `labels`           | [`AssistantLabels`](#assistantlabels) or `null`         | Applique un patch profond au texte visible par le client.                                              |
  | `supportEmail`     | string or `null`                                        | Change l'adresse d'assistance. Passez `null` pour la supprimer.                                        |
  | `starterQuestions` | string\[] or `null`                                     | Change jusqu'à trois invites. Passez `null` pour restaurer une liste vide.                             |
  | `filter`           | [`AssistantFilter`](#assistantfilter) or `null`         | Applique un patch en profondeur aux filtres de recherche. Passez `null` pour effacer tous les filtres. |
  | `hooks`            | [`AssistantHooks`](#assistanthooks) or `null`           | Applique un patch profond aux observateurs d'événements et d'erreurs.                                  |

  <div id="browser-api">
    ## API du navigateur
  </div>

  | Method                   | Parameter types                                       | Description                                                                                               |
  | ------------------------ | ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
  | `init(config)`           | [`AssistantConfig`](#assistantconfig)                 | Charge et monte le widget. Il s'agit de la promesse de disponibilité pour toutes les autres méthodes.     |
  | `open(options)`          | [`AssistantOpenOptions`](#assistantopenoptions)       | Ouvre la présentation configurée.                                                                         |
  | `close()`                | —                                                     | Ferme le widget.                                                                                          |
  | `ask(question, options)` | string, [`AssistantAskOptions`](#assistantaskoptions) | Ouvre le widget si demandé et envoie une question.                                                        |
  | `update(config)`         | [`AssistantUpdate`](#assistantupdate)                 | Applique un patch profond aux paramètres modifiables d'identité, d'apparence, de texte et d'observateurs. |
  | `reset()`                | —                                                     | Démarre une nouvelle conversation.                                                                        |
  | `destroy()`              | —                                                     | Supprime le widget et libère ses ressources navigateur.                                                   |

  Les instantanés de conversation restent privés au widget. Chaque méthode se résout en `void`.

  <div id="content-security-policy">
    ## Content Security Policy
  </div>

  Si votre site utilise une Content Security Policy, autorisez les origines requises par les fonctionnalités de votre widget activées :

  | Directive                                    | Source                              | Required for                      |
  | -------------------------------------------- | ----------------------------------- | --------------------------------- |
  | `script-src`                                 | `https://cdn.jsdelivr.net`          | Chargeur et runtime du widget     |
  | `connect-src`                                | `https://api.mintlify.com`          | API du widget                     |
  | `style-src`                                  | `https://cdn.jsdelivr.net`          | Feuille de style du widget        |
  | `font-src`                                   | `https://cdn.jsdelivr.net`          | Police Inter intégrée facultative |
  | `script-src`, `connect-src`, and `frame-src` | `https://challenges.cloudflare.com` | Protection anti-bot Turnstile     |
  | `script-src`                                 | `https://js.hcaptcha.com`           | Protection anti-bot hCaptcha      |
  | `connect-src` and `frame-src`                | `https://*.hcaptcha.com`            | Protection anti-bot hCaptcha      |

  Une politique `script-src` stricte doit toujours autoriser à la fois le chargeur et le script d'initialisation. Passer `nonce` à `init()` ne le propage qu'aux ressources créées par le widget après l'initialisation.
</AssistantWidgetPlayground>
