Widget

Widget

Installer le widget de feedback, comprendre quand il s'affiche, et le piloter depuis votre app.

Le widget, c'est la partie que vos clients voient : un bouton flottant qui leur permet de dessiner un rectangle sur la page, de taper un commentaire et de l'envoyer — épinglé à l'élément exact.

npm install github:gnoopy/instafix#widget-dist
import { initInstaFix } from "@instafix/widget";

const instafix = initInstaFix({
  endpoint: "/api/instafix",
  projectName: "mon-projet",
});

initInstaFix est l'export runtime principal du package, aux côtés des deux helpers i18n (registerLocale, loadLocale) et des types TypeScript. Il pèse environ 30 KB gzip (ESM), charge ses parties lourdes à la demande (panneau, locales, moteur de capture) et s'affiche dans un Shadow DOM fermé — les styles de votre page et ceux du widget ne peuvent jamais se marcher dessus.

Quand le widget s'affiche — et quand il ne s'affiche pas

initInstaFix exécute une série de garde-fous, dans cet ordre. Quand l'un d'eux s'active, vous récupérez une instance no-op (toutes les méthodes existent, rien ne s'affiche) :

  1. Rendu côté serveur — pas de window ? Le widget passe son tour avec onSkip("ssr"). Jamais contournable.
  2. Déjà initialisé — un second appel à initInstaFix() renvoie l'instance existante (un seul widget par page). destroy() remet le compteur à zéro.
  3. Production — quand process.env.NODE_ENV === "production", le widget passe son tour avec onSkip("production"). C'est le comportement « les clients le voient pendant la relecture, les visiteurs jamais ». Contournez-le avec forceShow: true pour les environnements de staging ou de preview. À noter : seul process.env.NODE_ENV est lu — pas import.meta.env.
  4. Petits écrans — en dessous de minViewportWidth (par défaut 768 px), il passe son tour avec onSkip("mobile"). Également contourné par forceShow.
  5. Validation de la config — pas d'endpoint/store, ou pas de projectName ? Le widget affiche un console.error et ne fait rien. Ce cas-là n'appelle pas onSkip — regardez la console, pas le callback.

L'instance

const instafix = initInstaFix({ ... });

instafix.open();                  // ouvrir le panneau de feedback
instafix.close();                 // le fermer
instafix.refresh();               // recharger les feedbacks de la page courante (ne lève jamais)
instafix.focusFeedback(id);       // faire défiler jusqu'à un marqueur et le mettre en évidence ; false si inconnu
const off = instafix.on("feedback:sent", (feedback) => { ... });
instafix.destroy();               // démontage complet, restaure tous les globals patchés

on() renvoie une fonction de désabonnement. Sept événements publics sont disponibles :

ÉvénementCharge utileDéclenché quand
feedback:sentle feedback crééUn envoi a réussi
feedback:deletedl'id du feedbackUn feedback a été supprimé
feedback:errorl'ErrorUn appel à l'API de feedback a échoué — même charge utile que le callback de config onError
panel:open / panel:closeLe panneau s'est ouvert / fermé
annotation:start / annotation:endUne session d'annotation a commencé / s'est terminée. end se déclenche que l'utilisateur ait envoyé ou annulé, et les deux points d'entrée — le flux de dessin du bouton flottant et le sélecteur de ciblage automatique de la barre d'outils — émettent la paire symétrique : de quoi mettre en pause vos bulles de chat ou vos overlays d'analytics entre les deux

React

Utilisez le hook dédié — il survit aux doubles montages du StrictMode et laisse les callbacks changer d'un rendu à l'autre sans réinitialiser :

"use client";
import { useInstaFix } from "@instafix/widget/react";

export function Feedback() {
  const instafix = useInstaFix({
    endpoint: "/api/instafix",
    projectName: "mon-projet",
    onFeedbackSent: (f) => console.log("nouveau feedback", f.id),
  });
  return null; // le widget se rend tout seul
}

Le hook renvoie null jusqu'à son montage, puis l'instance. Changer endpoint ou une autre option structurelle demande un remontage ; changer un callback, non — tous les callbacks acceptés par la config (onFeedbackSent, onError, onOpen, onClose, onAnnotationStart, onAnnotationEnd, onSkip) sont lus au moment de l'appel, donc un handler qui capture un état frais se déclenche avec cet état. Ils se taisent également après le démontage : une réponse tardive ne peut pas rappeler dans un arbre déjà démonté.

Gérer les erreurs

onError reçoit des erreurs avec deux champs utiles — code ("NETWORK" | "VALIDATION" | "AUTH" | "SERVER") et retryable (booléen) :

initInstaFix({
  endpoint: "/api/instafix",
  projectName: "mon-projet",
  onError: (error) => {
    const code = (error as { code?: string }).code;
    if (code === "AUTH") console.warn("vérifiez votre apiKey");
  },
});

Branchez sur error.code, pas sur instanceof — les classes d'erreur elles-mêmes ne sont pas exportées par le package. Quand l'utilisateur annule la demande d'identité, aucune erreur n'est émise : c'est une annulation, pas un échec.

Les statuts, côté widget

Un feedback a quatre statuts (open, in_progress, resolved, wont_fix). Le widget affiche les quatre mais ses propres actions sont volontairement binaires : résoudre et rouvrir. Les états intermédiaires appartiennent à votre flux de tri dans le dashboard.

La fiabilité, sans rien faire

  • Réessais avec backoff — les envois échoués sont retentés 3 fois (timeout de 10 s, backoff exponentiel avec jitter). Les erreurs serveur et les pannes réseau sont retentées, les erreurs de validation non.
  • File d'attente hors ligne — quand un envoi continue d'échouer, la charge utile est mise en file dans le localStorage (jusqu'à 20 entrées) et vidée au chargement suivant. La file ne stocke que les charges utiles — jamais de tokens ni d'en-têtes, qui sont recalculés au moment de la purge.
  • Les deux s'appliquent au mode HTTP (endpoint). En mode store côté client, les écritures sont locales : il n'y a rien à réessayer.
Modifier sur GitHub

Sur cette page