v1.0 - Version stable: multi-PC, détection UI-DETR-1, 3 modes exécution

- Frontend v4 accessible sur réseau local (192.168.1.40)
- Ports ouverts: 3002 (frontend), 5001 (backend), 5004 (dashboard)
- Ollama GPU fonctionnel
- Self-healing interactif
- Dashboard confiance

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Dom
2026-01-29 11:23:51 +01:00
parent 21bfa3b337
commit a27b74cf22
1595 changed files with 412691 additions and 400 deletions

View File

@@ -0,0 +1,252 @@
/**
* Messages d'Erreur en Français Clair
* Auteur : Dom, Alice, Kiro - 08 janvier 2026
*
* Ce module centralise tous les messages d'erreur et d'avertissement
* en français clair et compréhensible pour les utilisateurs.
*/
export interface ErrorMessage {
title: string;
description: string;
solution: string;
severity: 'critical' | 'high' | 'medium' | 'low';
}
// Messages d'erreur pour les paramètres manquants
export const missingParameterMessages: Record<string, ErrorMessage> = {
target: {
title: 'Élément cible non sélectionné',
description: 'Cette étape a besoin de savoir sur quel élément de la page agir.',
solution: 'Cliquez sur "Sélectionner un élément" et choisissez l\'élément cible sur la capture d\'écran.',
severity: 'high'
},
text: {
title: 'Texte à saisir manquant',
description: 'Cette étape de saisie ne sait pas quel texte écrire.',
solution: 'Saisissez le texte dans le champ "Texte à saisir". Vous pouvez utiliser des variables avec ${nom_variable}.',
severity: 'high'
},
duration: {
title: 'Durée d\'attente non définie',
description: 'Cette étape d\'attente ne sait pas combien de temps attendre.',
solution: 'Définissez une durée en secondes (par exemple : 2 pour 2 secondes, 0.5 pour 500 millisecondes).',
severity: 'high'
},
condition: {
title: 'Condition logique manquante',
description: 'Cette étape conditionnelle ne sait pas quelle condition évaluer.',
solution: 'Saisissez une expression logique comme "${age} >= 18" ou "${status} == \'actif\'".',
severity: 'high'
},
url: {
title: 'URL de destination manquante',
description: 'Cette étape de navigation ne sait pas vers quelle page aller.',
solution: 'Saisissez l\'URL complète (ex: https://example.com) ou utilisez une variable ${url}.',
severity: 'high'
},
selector: {
title: 'Sélecteur d\'élément manquant',
description: 'Cette étape ne sait pas quel élément de la page cibler.',
solution: 'Utilisez le sélecteur visuel pour choisir l\'élément ou saisissez un sélecteur CSS.',
severity: 'high'
},
attribute: {
title: 'Attribut à extraire non spécifié',
description: 'Cette étape d\'extraction ne sait pas quelle information récupérer.',
solution: 'Choisissez le type d\'information à extraire : texte, valeur, lien, etc.',
severity: 'medium'
}
};
// Messages d'erreur pour les problèmes de workflow
export const workflowErrorMessages: Record<string, ErrorMessage> = {
cycle_detected: {
title: 'Boucle infinie détectée',
description: 'Le workflow contient une boucle qui empêcherait son exécution normale.',
solution: 'Vérifiez les connexions entre les étapes et supprimez les références circulaires.',
severity: 'critical'
},
disconnected_step: {
title: 'Étape isolée',
description: 'Cette étape n\'est connectée à aucune autre et ne sera pas exécutée.',
solution: 'Connectez cette étape au flux principal ou supprimez-la si elle n\'est plus nécessaire.',
severity: 'medium'
},
invalid_variable_reference: {
title: 'Variable inexistante',
description: 'Une variable utilisée dans les paramètres n\'existe pas.',
solution: 'Créez la variable manquante ou corrigez le nom de la variable dans les paramètres.',
severity: 'high'
},
execution_blocked: {
title: 'Exécution impossible',
description: 'Des erreurs critiques empêchent le lancement du workflow.',
solution: 'Corrigez toutes les erreurs marquées en rouge avant de pouvoir exécuter le workflow.',
severity: 'critical'
},
no_start_step: {
title: 'Aucune étape de départ',
description: 'Le workflow n\'a pas d\'étape de départ clairement identifiée.',
solution: 'Assurez-vous qu\'au moins une étape n\'a pas de connexion d\'entrée pour servir de point de départ.',
severity: 'high'
},
multiple_start_steps: {
title: 'Plusieurs étapes de départ',
description: 'Le workflow a plusieurs étapes sans connexion d\'entrée.',
solution: 'Connectez les étapes pour n\'avoir qu\'un seul point de départ, ou utilisez une étape de condition.',
severity: 'medium'
}
};
// Messages d'erreur pour les variables
export const variableErrorMessages: Record<string, ErrorMessage> = {
invalid_name: {
title: 'Nom de variable invalide',
description: 'Le nom de la variable contient des caractères non autorisés.',
solution: 'Utilisez uniquement des lettres, chiffres et underscores. Commencez par une lettre.',
severity: 'high'
},
duplicate_name: {
title: 'Nom de variable déjà utilisé',
description: 'Une variable avec ce nom existe déjà.',
solution: 'Choisissez un nom différent ou modifiez la variable existante.',
severity: 'high'
},
empty_value: {
title: 'Valeur de variable vide',
description: 'La variable n\'a pas de valeur par défaut.',
solution: 'Définissez une valeur par défaut ou laissez la variable être remplie dynamiquement.',
severity: 'low'
},
invalid_type: {
title: 'Type de variable incorrect',
description: 'La valeur ne correspond pas au type déclaré de la variable.',
solution: 'Vérifiez que la valeur correspond au type (texte, nombre, booléen) de la variable.',
severity: 'medium'
}
};
// Messages d'erreur pour l'exécution
export const executionErrorMessages: Record<string, ErrorMessage> = {
step_failed: {
title: 'Échec de l\'étape',
description: 'L\'étape n\'a pas pu s\'exécuter correctement.',
solution: 'Vérifiez les paramètres de l\'étape et l\'état de la page web cible.',
severity: 'high'
},
element_not_found: {
title: 'Élément introuvable',
description: 'L\'élément ciblé n\'a pas été trouvé sur la page.',
solution: 'Vérifiez que la page est correctement chargée et que l\'élément existe toujours.',
severity: 'high'
},
timeout_exceeded: {
title: 'Délai d\'attente dépassé',
description: 'L\'étape a pris trop de temps à s\'exécuter.',
solution: 'Augmentez le délai d\'attente ou vérifiez que la page répond correctement.',
severity: 'medium'
},
network_error: {
title: 'Erreur de réseau',
description: 'Impossible de communiquer avec la page web ou le serveur.',
solution: 'Vérifiez votre connexion internet et que le site web est accessible.',
severity: 'high'
},
permission_denied: {
title: 'Permission refusée',
description: 'L\'action n\'est pas autorisée sur cette page ou cet élément.',
solution: 'Vérifiez les permissions du navigateur et les restrictions de sécurité de la page.',
severity: 'high'
}
};
// Messages d'avertissement
export const warningMessages: Record<string, ErrorMessage> = {
large_workflow: {
title: 'Workflow volumineux',
description: 'Ce workflow contient beaucoup d\'étapes et pourrait être lent à exécuter.',
solution: 'Considérez diviser le workflow en plusieurs parties plus petites.',
severity: 'low'
},
unused_variable: {
title: 'Variable non utilisée',
description: 'Cette variable est définie mais n\'est utilisée dans aucune étape.',
solution: 'Supprimez la variable si elle n\'est plus nécessaire ou utilisez-la dans une étape.',
severity: 'low'
},
deprecated_step: {
title: 'Type d\'étape obsolète',
description: 'Ce type d\'étape est obsolète et pourrait ne plus être supporté.',
solution: 'Remplacez par un type d\'étape plus récent avec des fonctionnalités équivalentes.',
severity: 'medium'
},
performance_warning: {
title: 'Avertissement de performance',
description: 'Cette configuration pourrait impacter les performances d\'exécution.',
solution: 'Optimisez les paramètres ou la structure du workflow pour de meilleures performances.',
severity: 'low'
}
};
// Fonction utilitaire pour obtenir un message d'erreur
export const getErrorMessage = (
category: 'parameter' | 'workflow' | 'variable' | 'execution' | 'warning',
errorType: string,
context?: Record<string, any>
): ErrorMessage => {
const messageMaps = {
parameter: missingParameterMessages,
workflow: workflowErrorMessages,
variable: variableErrorMessages,
execution: executionErrorMessages,
warning: warningMessages
};
const baseMessage = messageMaps[category]?.[errorType];
if (!baseMessage) {
return {
title: 'Erreur inconnue',
description: `Une erreur de type "${errorType}" s'est produite.`,
solution: 'Contactez le support technique si le problème persiste.',
severity: 'medium'
};
}
// Personnaliser le message avec le contexte si fourni
if (context) {
let customizedMessage = { ...baseMessage };
// Remplacer les placeholders dans les messages
Object.entries(context).forEach(([key, value]) => {
const placeholder = `{${key}}`;
customizedMessage.title = customizedMessage.title.replace(placeholder, String(value));
customizedMessage.description = customizedMessage.description.replace(placeholder, String(value));
customizedMessage.solution = customizedMessage.solution.replace(placeholder, String(value));
});
return customizedMessage;
}
return baseMessage;
};
// Messages de succès
export const successMessages: Record<string, string> = {
workflow_saved: 'Workflow sauvegardé avec succès',
workflow_executed: 'Workflow exécuté avec succès',
step_completed: 'Étape terminée avec succès',
validation_passed: 'Validation réussie, aucun problème détecté',
variable_created: 'Variable créée avec succès',
connection_established: 'Connexion établie entre les étapes'
};
// Messages informatifs
export const infoMessages: Record<string, string> = {
workflow_loading: 'Chargement du workflow en cours...',
step_executing: 'Exécution de l\'étape en cours...',
validation_running: 'Validation du workflow en cours...',
saving_workflow: 'Sauvegarde du workflow...',
connecting_backend: 'Connexion au serveur...'
};

View File

@@ -0,0 +1,76 @@
/**
* Suppression des erreurs ResizeObserver
* Auteur : Dom, Alice, Kiro - 09 janvier 2026
*
* Ce fichier supprime les erreurs ResizeObserver qui sont générées par
* les bibliothèques tierces (Material-UI, ReactFlow) et qui ne sont pas
* des erreurs critiques. Ces erreurs sont connues et documentées comme
* étant bénignes dans les environnements de développement.
*
* Référence: https://github.com/WICG/resize-observer/issues/38
*/
// Suppression globale des erreurs ResizeObserver
const suppressResizeObserverErrors = (): void => {
// Intercepter les erreurs de la fenêtre
const originalWindowError = window.onerror;
window.onerror = (message, source, lineno, colno, error) => {
if (
typeof message === 'string' &&
message.includes('ResizeObserver loop')
) {
// Ignorer silencieusement cette erreur spécifique
return true;
}
// Appeler le gestionnaire d'erreur original si présent
if (originalWindowError) {
return originalWindowError(message, source, lineno, colno, error);
}
return false;
};
// Intercepter les erreurs non gérées
window.addEventListener('error', (event: ErrorEvent) => {
if (
event.message &&
event.message.includes('ResizeObserver loop')
) {
event.preventDefault();
event.stopPropagation();
event.stopImmediatePropagation();
return;
}
}, true);
// Intercepter les promesses rejetées
window.addEventListener('unhandledrejection', (event: PromiseRejectionEvent) => {
if (
event.reason &&
typeof event.reason === 'object' &&
event.reason.message &&
event.reason.message.includes('ResizeObserver loop')
) {
event.preventDefault();
return;
}
});
// Intercepter console.error pour les erreurs ResizeObserver
const originalConsoleError = console.error;
console.error = (...args: unknown[]) => {
const message = args[0];
if (
typeof message === 'string' &&
message.includes('ResizeObserver loop')
) {
// Ignorer silencieusement
return;
}
originalConsoleError.apply(console, args);
};
};
// Exécuter immédiatement
suppressResizeObserverErrors();
export default suppressResizeObserverErrors;

View File

@@ -0,0 +1,275 @@
/**
* Système de Tooltips Explicatifs en Français
* Auteur : Dom, Alice, Kiro - 08 janvier 2026
*
* Ce module centralise tous les tooltips explicatifs en français pour
* assurer la cohérence linguistique et faciliter la maintenance.
*/
export interface TooltipContent {
title: string;
description: string;
example?: string;
shortcut?: string;
}
// Tooltips pour les types d'étapes
export const stepTooltips: Record<string, TooltipContent> = {
click: {
title: 'Cliquer sur un élément',
description: 'Effectue un clic sur un élément de la page web. Vous pouvez choisir le type de clic (gauche, droit, double-clic).',
example: 'Cliquer sur un bouton "Valider" ou un lien',
shortcut: 'Glisser depuis la palette'
},
type: {
title: 'Saisir du texte',
description: 'Saisit du texte dans un champ de saisie. Supporte les variables dynamiques avec la syntaxe ${nom_variable}.',
example: 'Saisir "Bonjour ${nom_utilisateur}" dans un champ',
shortcut: 'Glisser depuis la palette'
},
wait: {
title: 'Attendre',
description: 'Met en pause l\'exécution pendant une durée spécifiée. Utile pour attendre le chargement d\'éléments.',
example: 'Attendre 2 secondes après un clic',
shortcut: 'Glisser depuis la palette'
},
condition: {
title: 'Condition logique',
description: 'Exécute des actions différentes selon une condition. Utilise des expressions logiques simples.',
example: 'Si ${age} > 18 alors continuer',
shortcut: 'Glisser depuis la palette'
},
extract: {
title: 'Extraire des données',
description: 'Extrait des données depuis un élément de la page (texte, valeur, lien, etc.) et les stocke dans une variable.',
example: 'Extraire le prix d\'un produit',
shortcut: 'Glisser depuis la palette'
},
scroll: {
title: 'Faire défiler la page',
description: 'Fait défiler la page dans une direction donnée. Utile pour révéler des éléments cachés.',
example: 'Défiler vers le bas de 300 pixels',
shortcut: 'Glisser depuis la palette'
},
navigate: {
title: 'Naviguer vers une URL',
description: 'Navigue vers une nouvelle page web. Supporte les variables dans l\'URL.',
example: 'Aller à https://example.com/user/${id}',
shortcut: 'Glisser depuis la palette'
},
screenshot: {
title: 'Capturer l\'écran',
description: 'Prend une capture d\'écran de la page actuelle. Utile pour documenter ou déboguer.',
example: 'Capturer avant et après une action',
shortcut: 'Glisser depuis la palette'
}
};
// Tooltips pour les catégories d'étapes
export const categoryTooltips: Record<string, TooltipContent> = {
'actions-web': {
title: 'Actions Web',
description: 'Étapes pour interagir directement avec les éléments des pages web (clics, saisie, navigation).',
example: 'Cliquer, saisir du texte, naviguer'
},
'logique': {
title: 'Logique',
description: 'Structures de contrôle pour créer des workflows intelligents avec conditions et branchements.',
example: 'Conditions if/else, boucles'
},
'donnees': {
title: 'Données',
description: 'Étapes pour extraire, manipuler et stocker des informations depuis les pages web.',
example: 'Extraire prix, texte, liens'
},
'controle': {
title: 'Contrôle',
description: 'Étapes pour contrôler le flux d\'exécution et la synchronisation du workflow.',
example: 'Attendre, capturer, synchroniser'
}
};
// Tooltips pour les paramètres
export const parameterTooltips: Record<string, TooltipContent> = {
target: {
title: 'Élément cible',
description: 'L\'élément de la page sur lequel effectuer l\'action. Utilisez le sélecteur visuel pour le choisir facilement.',
example: 'Bouton, champ de saisie, lien'
},
text: {
title: 'Texte à saisir',
description: 'Le texte qui sera saisi dans le champ. Vous pouvez utiliser des variables avec ${nom_variable}.',
example: 'Bonjour ${nom} ou texte fixe'
},
duration: {
title: 'Durée d\'attente',
description: 'Temps d\'attente en secondes. Peut être décimal (ex: 1.5 pour 1,5 seconde).',
example: '2 pour 2 secondes, 0.5 pour 500ms'
},
condition: {
title: 'Expression conditionnelle',
description: 'Expression logique à évaluer. Utilise les opérateurs ==, !=, >, <, >=, <= et les variables.',
example: '${age} >= 18 ou ${status} == "actif"'
},
clickType: {
title: 'Type de clic',
description: 'Le type de clic à effectuer sur l\'élément cible.',
example: 'Clic gauche pour la plupart des actions'
},
clearFirst: {
title: 'Vider le champ d\'abord',
description: 'Si activé, vide le contenu existant du champ avant de saisir le nouveau texte.',
example: 'Utile pour remplacer du texte existant'
},
attribute: {
title: 'Attribut à extraire',
description: 'Le type d\'information à extraire de l\'élément sélectionné.',
example: 'Texte visible, valeur du champ, URL du lien'
},
direction: {
title: 'Direction du défilement',
description: 'La direction dans laquelle faire défiler la page.',
example: 'Vers le bas pour voir plus de contenu'
},
amount: {
title: 'Quantité de défilement',
description: 'Distance de défilement en pixels. Plus la valeur est élevée, plus le défilement est important.',
example: '300 pixels = environ 1/3 d\'écran'
},
url: {
title: 'URL de destination',
description: 'L\'adresse web vers laquelle naviguer. Peut contenir des variables.',
example: 'https://site.com/page/${id}'
},
filename: {
title: 'Nom du fichier de capture',
description: 'Nom optionnel pour le fichier de capture d\'écran. Si vide, un nom automatique sera généré.',
example: 'capture_${timestamp} ou nom_fixe'
}
};
// Tooltips pour l'interface utilisateur
export const uiTooltips: Record<string, TooltipContent> = {
canvas: {
title: 'Zone de travail',
description: 'Espace principal où vous construisez votre workflow en glissant des étapes et en les connectant.',
shortcut: 'Molette pour zoomer, clic-glisser pour déplacer'
},
palette: {
title: 'Palette d\'étapes',
description: 'Boîte à outils contenant tous les types d\'étapes disponibles, organisés par catégories.',
shortcut: 'Glisser une étape vers le canvas'
},
properties: {
title: 'Panneau de propriétés',
description: 'Configuration des paramètres de l\'étape sélectionnée. Chaque type d\'étape a ses propres paramètres.',
shortcut: 'Cliquer sur une étape pour la configurer'
},
minimap: {
title: 'Mini-carte',
description: 'Vue d\'ensemble du workflow complet. Utile pour naviguer dans les gros workflows.',
shortcut: 'Cliquer pour se déplacer rapidement'
},
validator: {
title: 'Validateur',
description: 'Vérifie la validité du workflow et signale les erreurs ou avertissements.',
example: 'Paramètres manquants, étapes déconnectées'
},
executor: {
title: 'Exécuteur',
description: 'Lance l\'exécution du workflow et affiche le progrès en temps réel.',
shortcut: 'Bouton Play pour démarrer'
},
variables: {
title: 'Gestionnaire de variables',
description: 'Créez et gérez les variables utilisées dans votre workflow pour le rendre dynamique.',
example: 'nom_utilisateur, email, compteur'
},
documentation: {
title: 'Documentation interactive',
description: 'Guides et exemples pour apprendre à utiliser le Visual Workflow Builder efficacement.',
shortcut: 'Onglet Documentation'
},
visualSelector: {
title: 'Sélecteur visuel',
description: 'Outil pour sélectionner visuellement des éléments sur une capture d\'écran de la page web.',
shortcut: 'Bouton "Sélectionner un élément"'
},
search: {
title: 'Recherche d\'étapes',
description: 'Recherchez rapidement un type d\'étape par son nom ou sa description.',
shortcut: 'Ctrl+F dans la palette'
}
};
// Tooltips pour les raccourcis clavier
export const keyboardTooltips: Record<string, TooltipContent> = {
'ctrl+z': {
title: 'Annuler',
description: 'Annule la dernière action effectuée dans le workflow.',
shortcut: 'Ctrl+Z'
},
'ctrl+y': {
title: 'Rétablir',
description: 'Rétablit la dernière action annulée.',
shortcut: 'Ctrl+Y'
},
'ctrl+s': {
title: 'Sauvegarder',
description: 'Sauvegarde le workflow actuel.',
shortcut: 'Ctrl+S'
},
'ctrl+c': {
title: 'Copier',
description: 'Copie les étapes sélectionnées.',
shortcut: 'Ctrl+C'
},
'ctrl+v': {
title: 'Coller',
description: 'Colle les étapes copiées.',
shortcut: 'Ctrl+V'
},
'delete': {
title: 'Supprimer',
description: 'Supprime les étapes ou connexions sélectionnées.',
shortcut: 'Suppr'
},
'ctrl+a': {
title: 'Tout sélectionner',
description: 'Sélectionne toutes les étapes du workflow.',
shortcut: 'Ctrl+A'
},
'space': {
title: 'Mode panoramique',
description: 'Maintenir Espace + glisser pour déplacer la vue du canvas.',
shortcut: 'Espace + glisser'
}
};
// Fonction utilitaire pour obtenir un tooltip
export const getTooltip = (category: string, key: string): TooltipContent | null => {
const tooltipMaps: Record<string, Record<string, TooltipContent>> = {
step: stepTooltips,
category: categoryTooltips,
parameter: parameterTooltips,
ui: uiTooltips,
keyboard: keyboardTooltips
};
return tooltipMaps[category]?.[key] || null;
};
// Fonction pour formater un tooltip en texte riche
export const formatTooltip = (tooltip: TooltipContent): string => {
let formatted = `${tooltip.title}\n\n${tooltip.description}`;
if (tooltip.example) {
formatted += `\n\nExemple : ${tooltip.example}`;
}
if (tooltip.shortcut) {
formatted += `\n\nRaccourci : ${tooltip.shortcut}`;
}
return formatted;
};