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-distimport { 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) :
- Rendu côté serveur — pas de
window? Le widget passe son tour aveconSkip("ssr"). Jamais contournable. - Déjà initialisé — un second appel à
initInstaFix()renvoie l'instance existante (un seul widget par page).destroy()remet le compteur à zéro. - Production — quand
process.env.NODE_ENV === "production", le widget passe son tour aveconSkip("production"). C'est le comportement « les clients le voient pendant la relecture, les visiteurs jamais ». Contournez-le avecforceShow: truepour les environnements de staging ou de preview. À noter : seulprocess.env.NODE_ENVest lu — pasimport.meta.env. - Petits écrans — en dessous de
minViewportWidth(par défaut 768 px), il passe son tour aveconSkip("mobile"). Également contourné parforceShow. - Validation de la config — pas d'
endpoint/store, ou pas deprojectName? Le widget affiche unconsole.erroret ne fait rien. Ce cas-là n'appelle pasonSkip— 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éson() renvoie une fonction de désabonnement. Sept événements publics sont disponibles :
| Événement | Charge utile | Déclenché quand |
|---|---|---|
feedback:sent | le feedback créé | Un envoi a réussi |
feedback:deleted | l'id du feedback | Un feedback a été supprimé |
feedback:error | l'Error | Un appel à l'API de feedback a échoué — même charge utile que le callback de config onError |
panel:open / panel:close | — | Le panneau s'est ouvert / fermé |
annotation:start / annotation:end | — | Une 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 modestorecôté client, les écritures sont locales : il n'y a rien à réessayer.