89 Commits
Author SHA1 Message Date
Pierre Marx ae11b45e4a chore: bump version to v2.0.2 2026-03-19 12:42:10 -04:00
pierre 8f68d557a7 Merge pull request 'feat: aligner event handler Socket.IO sur le contrat snake_case (#20)' (#21) from feat/#20-snake-case-events into feat/prd-19-snake-case-events 2026-03-19 17:40:23 +01:00
Pierre Marx 72514cc578 feat: aligner event handler Socket.IO sur le contrat snake_case (#20)
- Ecouter call_event au lieu de ipbx_event
- Lire event_code, queue_name, terminal en snake_case
- handleCallPickedUp/handleCallHungUp utilisent event.queue_name
- renderer.js lit data.queue_name
- 3 tests unitaires ajoutés dans socketio-adapter.test.js
2026-03-19 12:32:30 -04:00
pierre 78105f84d1 Merge pull request 'feat: migration complète SignalR → Socket.IO (PRD #2)' (#18) from feat/prd-2-socketio-migration into main 2026-03-19 17:18:47 +01:00
Pierre Marx a6b33bf3cb fix: annuler le timeout logoff quand logout_ok arrive
clearTimeout sur le fallback 5s pour éviter un disconnect()
parasite sur un socket déjà fermé.
2026-03-19 12:15:34 -04:00
Pierre Marx e69bd357ff fix: ajouter couleur yellow manquante dans l'objet ANSI
c.yellow était utilisé ligne 222 mais jamais défini, produisant
"undefined" dans les logs au lieu du code ANSI jaune.
2026-03-19 12:12:13 -04:00
Pierre Marx ef3f3e3081 fix: charger les terminaux si le serveur est déjà connecté au démarrage
Sur réseau local rapide, le health check répondait avant que le
renderer enregistre son listener — l'event 'connected' était perdu
et loadTerminals() jamais appelé. Bump v2.0.1.
2026-03-18 23:40:32 -04:00
pierre b7d0892e46 Merge pull request 'feat: migration complète SignalR → Socket.IO (PRD #2)' (#14) from feat/#7-cleanup-signalr into feat/prd-2-socketio-migration 2026-03-19 03:45:09 +01:00
Pierre Marx 75deb77f20 refactor: supprimer handlers IPC orphelins (closes #17)
simulate-call, get-simulated-calls et is-development étaient des
restes de cti-simulator.js supprimé — plus appelés depuis renderer.js.
2026-03-18 22:42:47 -04:00
Pierre Marx 9258ae0a00 refactor: renommer tous les vestiges SignalR → server (closes #16)
- signalr-indicator → server-indicator (CSS, HTML, JS)
- signalrIndicator/signalrText → serverIndicator/serverText (HTML, JS)
- signalr-status → server-status (CSS, HTML)
- Supprimé docs obsolètes (INTEGRATION_SIGNALR, WORKFLOW_AGENT, changelog)
- Supprimé releases notes anciennes (v1.2.16, v1.3.0, v1.5.0)
- Supprimé README.md, styles.css, .claude/commands/dev.md (obsolètes)
2026-03-18 22:39:48 -04:00
Pierre Marx 54ee59681e feat: supprimer le checkbox Débloquer devenu inutile (closes #15)
Le serveur Python gère nativement le cleanup des sessions orphelines
au login — plus besoin de forceDisconnect côté client.
2026-03-18 22:31:01 -04:00
Pierre Marx a2784b385d feat: cleanup SignalR — supprimer anciens adaptateurs (closes #7)
- Supprimé connection-manager.js, websocket-adapter.js,
  rest-socket-adapter.js, cti-simulator.js
- Retiré @microsoft/signalr de package.json
- Seul socketio-adapter.js reste comme couche de communication
2026-03-18 21:29:34 -04:00
Pierre Marx d1275b40b0 refactor: unifier tous les logs via log() — console + fichier
Remplace tous les console.log/console.error par log() pour
que chaque message soit enregistré dans le fichier de log
(~/.simpleconnect-ng/socketio.log) en plus de la console.
2026-03-18 21:26:04 -04:00
Pierre Marx 25f0f2f078 docs: ajouter section tests dans CLAUDE.md 2026-03-18 20:41:24 -04:00
Pierre Marx cd76e71a5e test: tests unitaires socketio-adapter (8 tests, bun test)
- Injection socket factory pour testabilité (2e param constructeur)
- Tests : état initial, connect, login_error, connect_error,
  reconnecting, reconnexion, logoff, onStateChange
- Script test ajouté dans package.json
2026-03-18 20:38:57 -04:00
Pierre Marx 9e683a04e9 feat: désactiver le formulaire de login quand le serveur est injoignable
- Bouton et select disabled par défaut au démarrage
- Réactivés uniquement quand le health check confirme la connexion
- Pastille orange pulsante + "Reconnexion en cours..." au lieu de rouge fixe
- Style disabled grisé pour le bouton et le select
- resetLoginForm ne force plus disabled=false
2026-03-18 20:33:22 -04:00
Pierre Marx d377ed3ea8 fix: accents manquants sur les statuts serveur (connecté, déconnecté, configuré) 2026-03-18 20:26:14 -04:00
Pierre Marx f49e4bb1e1 feat: logs structurés avec couleurs ANSI et format lisible
- Banner au démarrage avec version et URL serveur
- Pastilles ✓/✗ pour connexion/déconnexion serveur
- Terminaux récupérés fusionnés dans la ligne de connexion
- Timestamps hh:mm:ss en console, date complète dans le fichier log
- Strip ANSI dans le fichier log pour lisibilité SSH
- Log de fermeture avant app.quit()
- Terminaux chargés uniquement quand le serveur est joignable
2026-03-18 20:25:13 -04:00
Pierre Marx 42be82ed8c fix: ne charger les terminaux que quand le serveur est joignable
Supprime le loadTerminals() au démarrage et à chaque changement de
statut. Les terminaux ne sont récupérés que lorsque le health check
confirme la connexion (statut passe à 'connected').
2026-03-18 20:10:41 -04:00
Pierre Marx 77a310976b feat: migration Socket.IO natif — login, terminaux REST, health check (closes #3)
Remplace toute la couche SignalR par une connexion Socket.IO directe
au serveur Python (port 8004). Auth au handshake, reconnexion native
illimitée, terminaux via REST GET /terminals.

- socketio-adapter.js : connect/logoff/disconnect, events login_ok/login_error
- main.js : initializeSocketIO, health check net.request, terminaux REST
- renderer.js : IPC signalr-status → server-status
- config.json : clé socketio (plus signalR)
- Version 2.0.0
2026-03-18 17:31:30 -04:00
Pierre Marx 630f1fa8c3 Condenser CLAUDE.md : 149→47 lignes, npm→bun
Supprimer les sections documentation (fonctionnalités, IPC détaillé,
webviews, workflow résumé) — garder uniquement le contexte essentiel.
2026-03-12 19:48:04 -04:00
Pierre Marx 71cfebcf2f release: Version 1.5.0 - Support serveur Python (REST + Socket.IO)
- Ajout de RestSocketAdapter pour le serveur Python
- Fallback automatique SignalR -> REST + Socket.IO
- Notes de release v1.5.0
2025-11-24 16:38:25 -05:00
Pierre Marx 2258013394 feat: add REST + Socket.IO fallback for Python server
Add RestSocketAdapter that uses:
- REST API for actions (login, logout, terminals)
- Socket.IO for real-time events (IpbxEvent)

ConnectionManager now tries SignalR first (.NET server),
then falls back to REST+SocketIO (Python server).

This enables the client to work with both servers during migration.
2025-11-24 16:05:30 -05:00
Pierre Marx 60bad93f1e docs: Mise à jour du README.md vers la version 1.4.1
- Mise à jour de la version actuelle (1.2.15 → 1.4.1)
- Ajout de l'historique des versions 1.3.0, 1.3.1, 1.4.0 et 1.4.1
- Documentation du support dual SignalR/WebSocket avec fallback automatique
- Ajout des nouveaux fichiers connection-manager.js et websocket-adapter.js
- Mise à jour de la stack technologique (socket.io-client 4.8.1)
- Correction des canaux IPC (login-agent, get-terminal-list)
- Ajout de nouveaux canaux (get-app-version, switch-to-center, release-center)
- Documentation du logging détaillé dans ~/.simpleconnect-ng/signalr.log
- Enrichissement de la section dépannage (5 scénarios au lieu de 3)
- Correction "Débloquer" au lieu de "Forcer la déconnexion"
- Ajout de la saisie manuelle des postes téléphoniques
- Suppression des références aux fichiers de documentation inexistants

Le README.md reflète maintenant fidèlement l'état du code en version 1.4.1.
2025-10-21 15:16:35 -04:00
Pierre Marx 3aeb684327 command commencer 2025-10-21 15:16:04 -04:00
Pierre Marx 7182d7af32 docs: Réorganisation complète de dev.md pour meilleure lisibilité
- Ajout d'une table des matières avec liens d'ancrage
- Résumé rapide des 8 phases en début de document
- Organisation claire par phases numérotées (1 à 8)
- Section 'Référence des commandes' regroupant :
  - Commandes npm
  - Commandes Git
  - Commandes tea (Gitea CLI)
  - Conventions de commits
  - Semantic Versioning
- Amélioration de la mise en forme et de la hiérarchie
- Suppression des redondances
- Date de dernière mise à jour ajoutée
2025-10-21 15:08:17 -04:00
Pierre Marx 6247888770 docs: Ajout de la Phase 6 - Publication sur Gitea
- Étapes détaillées pour créer tags Git et releases Gitea
- Utilisation de l'outil CLI 'tea' pour la gestion des releases
- Commande complète avec upload de tous les assets en une fois
- Procédures de vérification et correction (ajout/suppression assets)
- Section 'Commandes tea utiles' avec exemples pratiques
- Mise à jour du résumé du workflow (16 étapes au total)
2025-10-21 15:03:14 -04:00
Pierre Marx 6b13d5be51 docs: Déplacement du workflow détaillé vers .claude/commands/dev.md
- Suppression du workflow complet de CLAUDE.md (152 lignes)
- Ajout d'une référence vers .claude/commands/dev.md
- Ajout d'un résumé rapide en 6 points
- Conservation des points de vigilance essentiels
- Évite la duplication et facilite la maintenance
2025-10-21 14:25:07 -04:00
Pierre Marx 8e0b3f56c2 command dev pour claude 2025-10-21 14:21:16 -04:00
Pierre Marx 58bd71d03e docs: Ajout des notes de release pour v1.4.1 2025-10-21 13:46:30 -04:00
Pierre Marx 407ef2ee6c Merge branch 'feature/bouton-quitter-login' 2025-10-21 13:21:31 -04:00
Pierre Marx 339e9acf3c release: Version 1.4.1 - Bouton Quitter sur la page de login
- Ajout du bouton "Quitter" sur la page de connexion
- Fermeture propre de l'application sans connexion
- Style secondaire gris pour différenciation visuelle
- Bump version 1.4.1
2025-10-21 13:21:10 -04:00
Pierre Marx 22e68e9fd2 feat: Ajout d'un bouton Quitter sur la page de login
- Nouveau bouton "Quitter" sous le bouton "Se connecter"
- Permet de fermer proprement l'application sans se connecter
- Style gris secondaire pour différencier du bouton principal
- Appel à ipcRenderer.invoke('quit-app') pour fermer l'application

Fichiers modifiés :
- index.html : ajout du bouton avec id quitLoginBtn
- renderer.js : gestionnaire handleQuitFromLogin
- styles-modern.css : style .btn-quit avec couleur grise
2025-10-21 11:55:51 -04:00
Pierre Marx fc8dd1907f docs: Ajout des notes de release pour v1.4.0 2025-10-21 11:46:10 -04:00
Pierre Marx 2aa290056a Merge branch 'feature/affichage-version' 2025-10-21 11:42:24 -04:00
Pierre Marx c64fe44cf0 release: Version 1.4.0 - Affichage de la version
- Affichage de la version dans l'interface (login et header)
- Version dans le titre de la fenêtre native
- Handler IPC pour exposer la version
- Styles CSS pour affichage élégant
- Bump version 1.4.0
2025-10-21 11:41:44 -04:00
Pierre Marx 83e879f49f fix: Forcer l'affichage de la version dans le titre de la fenêtre
- Ajout d'un listener 'did-finish-load' pour forcer le titre après le chargement HTML
- Résout le problème où le <title> HTML écrasait le titre défini dans BrowserWindow
- Le titre "SimpleConnect vX.X.X" s'affiche maintenant correctement dans la barre native

Fichiers modifiés :
- main.js : ajout du listener did-finish-load avec setTitle
2025-10-21 11:38:16 -04:00
Pierre Marx c5a7ae3cc4 refactor: Version centrée sous le titre sur la page de login
- Déplacement de la version sous le titre "SimpleConnect" au lieu d'à côté
- Titre "SimpleConnect" maintenant parfaitement centré
- Nouvelle classe CSS .app-version-login pour le style de la version
- Version affichée en gris clair, centrée, taille 14px
- Amélioration de l'esthétique de la page de connexion

Fichiers modifiés :
- index.html : restructuration avec div séparée pour la version
- styles-modern.css : ajout du style .app-version-login
2025-10-21 11:34:30 -04:00
Pierre Marx 7b9679e4dc feat: Affichage de la version dans l'interface et la barre de titre
- Ajout de l'affichage de la version à côté du logo dans l'interface
- Ajout de la version dans le titre de la fenêtre (barre macOS/Windows/Linux)
- Création du handler IPC get-app-version pour exposer la version
- Mise à jour dynamique du titre lors de la connexion/déconnexion agent
- Style élégant pour la version affichée dans l'interface (gris clair, opacité 0.8)

Fichiers modifiés :
- index.html : ajout du span pour la version
- renderer.js : récupération et affichage de la version via IPC
- main.js : handler IPC et mise à jour des titres de fenêtre
- styles-modern.css : style pour .app-version
2025-10-21 11:30:27 -04:00
Pierre Marx 057504a65f docs: Amélioration du workflow de release dans CLAUDE.md
- Ajout du workflow complet de A à Z (6 phases détaillées)
- Phase 1: Développement sur branche feature
- Phase 2: Préparation de la release (changelog + version)
- Phase 3: Fusion dans main avec --no-ff
- Phase 4: Build des exécutables multi-plateformes
- Phase 5: Documentation finale (notes de release)
- Phase 6: Déploiement en production
- Ajout de la structure finale attendue
- Points de vigilance et bonnes pratiques
- Références à Semantic Versioning et Conventional Commits
2025-10-21 11:17:10 -04:00
Pierre Marx e601fef239 docs: Ajout des notes de release pour v1.3.1
- Documentation complète des nouvelles fonctionnalités
- Guide d'utilisation de la saisie manuelle de postes
- Liste des fichiers disponibles pour chaque plateforme
2025-10-21 11:12:03 -04:00
Pierre Marx a10e79fb11 Merge branch 'feature/saisie-manuelle-poste' 2025-10-17 16:12:27 -04:00
Pierre Marx a62f97f6e8 release: Version 1.3.1 - Saisie manuelle postes + améliorations UX
- Ajout de la saisie manuelle de postes téléphoniques personnalisés
- Suppression du préfixe 'Poste' dans l'affichage des terminaux
- Amélioration du contraste de l'élément survolé dans la liste
- Bump version 1.3.1
2025-10-17 16:11:58 -04:00
Pierre Marx b5d452934a fix: Améliorer le contraste de l'élément survolé dans la liste des postes
- Fond bleu clair (#e8f0fe) au lieu de bleu foncé (#667eea)
- Texte noir foncé (#1a1a1a) au lieu de blanc
- Ajout d'un poids de police semi-gras (500) pour meilleure lisibilité
- Résout le problème d'illisibilité lors de la navigation au clavier
2025-10-17 16:09:49 -04:00
Pierre Marx faaa0e9739 feat: Permettre la saisie manuelle d'un poste téléphonique personnalisé
- Ajout de addItems et addChoices dans Choices.js pour permettre la saisie manuelle
- Remplacement de la validation stricte par une validation de format numérique
- Ajout d'un avertissement visuel temporaire (5s) si le poste n'est pas dans la liste officielle
- Amélioration des textes UX pour guider l'utilisateur
- Le serveur accepte maintenant tout numéro de poste valide (numérique)
2025-10-17 11:39:13 -04:00
Pierre Marx 739cd65681 refactor: Enlever le préfixe 'Poste' de l'affichage des terminaux
- Affichage direct du numéro de terminal sans préfixe
- Modifié dans Choices.js et dans les fallbacks natifs
- Interface plus épurée et compacte
2025-10-17 11:34:12 -04:00
Pierre Marx ea305ad55e docs: Ajout des notes de release pour v1.2.16 et v1.3.0
- Création du dossier releases/ pour les notes de version
- v1.2.16.md : Version stable avec SignalR uniquement
- v1.3.0.md : Version avec support dual SignalR/SocketIO
- Ces notes serviront pour les releases Gitea
2025-09-24 15:38:32 -04:00
pierre 121d3f928f Merge pull request 'socketio-fallback-signalR' (#1) from socketio-fallback-signalR into main
Reviewed-on: pierre/SimpleConnect-client-electron#1
2025-09-24 18:09:08 +00:00
Pierre Marx 5b1c3a3eeb chore: Officialiser la version 1.3.0 avec support dual SignalR/SocketIO
- Ajout de l'entrée 1.3.0 dans le changelog (datée du 12 septembre 2025)
- Mise à jour de la version dans package.json de 1.2.16 vers 1.3.0
- Documentation complète du support dual protocole (SignalR/SocketIO)
- ConnectionManager avec fallback automatique
- WebSocketAdapter pour émulation de l'API SignalR
- Compatibilité totale avec backends .NET et Python
2025-09-24 14:08:25 -04:00
Pierre Marx c6aa113ddc fix: Corrige la déconnexion SignalR pour compatibilité serveur .NET
- Supprime l'appel à disconnect() qui envoyait un CloseMessage non supporté
- Laisse la connexion se fermer naturellement avec l'application
- Comportement identique au client WPF de production
- Résout l'erreur 'Received unsupported message: CloseMessage' sur le serveur
2025-09-11 20:40:34 -04:00
Pierre Marx b260c6ed0b première version avec socketio 2025-09-11 19:55:15 -04:00
Pierre Marx bc6f95ce1d chore: Simplifier la configuration de build Linux
- Garder uniquement AppImage comme target pour Linux
- Supprimer les targets deb et rpm non nécessaires
- Architecture x64 uniquement pour le moment
2025-09-05 10:19:20 -04:00
Pierre Marx 58172f6895 docs: Refonte complète du fichier CLAUDE.md pour guider les futures instances
- Structure complète avec commandes, architecture et workflow
- Documentation détaillée de l'intégration SignalR et IPC
- Instructions pour le système de logging et les webviews
- Points d'attention pour sécurité, UI/UX et build cross-platform
- Ajout d'un rappel important de ne pas mentionner Claude/Anthropic dans les commits
2025-09-05 09:58:35 -04:00
Pierre Marx ed6d1dce14 docs: Ajout du guide de workflow de développement CLAUDE.md
- Documentation du processus de finalisation des modifications
- Workflow en 4 étapes : changelog → version → commit → build
- Convention de versioning SemVer
- Guidelines pour les messages de commit
- Instructions pour le build multi-plateforme
2025-09-05 09:53:14 -04:00
Pierre Marx 2ad8d06a32 feat: Ajout système de logging SignalR, icônes SVG et suppression menu
- Système de logging SignalR complet dans ~/.simpleconnect-ng/signalr.log
- Capture de tous les événements SignalR avec format JSON structuré
- Remplacement des emojis par icônes SVG pour compatibilité Linux
- Suppression complète de la barre de menu Electron
- Configuration build Linux multi-architecture (x64/arm64)
- Support AppImage, .deb et .rpm
- Bump version 1.2.16
2025-09-05 09:49:27 -04:00
Pierre Marx 71b3e874c1 fix: Repositionnement des notifications pour ne plus cacher les boutons
- Décalage des notifications de top:20px à top:70px
- Les notifications apparaissent maintenant sous la barre d'outils
- Les boutons restent accessibles pendant l'affichage des notifications
- Mise à jour du changelog
2025-09-04 17:07:59 -04:00
Pierre Marx 931724b0bc refactor: Nettoyage complet du fichier config.json
- Suppression des sections obsolètes (agents, centres, CTI, preferences)
- Conservation uniquement de la configuration SignalR nécessaire
- Les agents et centres sont maintenant gérés côté serveur via SignalR
- Réduction du fichier de 118 lignes à 8 lignes (93% de réduction)
- terminalsSimulation conservé comme fallback pour le mode dégradé
- Mise à jour du changelog pour documenter ce nettoyage
2025-09-04 17:03:34 -04:00
Pierre Marx 69683fe1d0 docs: Refonte complète du README avec documentation à jour
- README entièrement réécrit avec l'état actuel de l'application (v1.2.15)
- Ajout des fonctionnalités actuelles : SignalR, CTI, panneau de notes redimensionnable
- Architecture technique détaillée avec stack technologique réelle
- Workflow d'utilisation complet de la connexion à la déconnexion
- Section dépannage avec problèmes courants et solutions
- Structure moderne avec emojis et organisation claire
- Mise à jour du changelog pour documenter cette refonte
2025-09-04 17:00:24 -04:00
Pierre Marx 506968b62a feat: Remplacer le bouton "Déconnecter" par "Quitter" avec fermeture complète
- Changement du texte du bouton de "Déconnexion" à "Quitter"
- Le bouton effectue maintenant une déconnexion puis ferme l'application
- Ajout d'un handler IPC 'quit-app' pour fermer proprement l'application
- Fermeture propre de la connexion SignalR avant l'arrêt
- Conservation de l'animation de déconnexion pour une transition fluide
- Mise à jour du changelog avec la version 1.2.15
2025-09-04 16:57:32 -04:00
Pierre Marx 06b4e2819d feat: Système de persistance des notes amélioré avec fichier unique par agent
- Un seul fichier notes_{agentId}.json par agent (plus d'accumulation)
- Auto-save après 2 secondes d'inactivité
- Restauration automatique au démarrage depuis fichier ou localStorage
- Historique des 50 dernières versions intégré dans le fichier
- Synchronisation transparente fichier/localStorage
- Notifications visuelles lors de la restauration
2025-09-04 16:49:07 -04:00
Pierre Marx 0aaa3e63f2 fix: Correction du panneau de notes partiellement visible au démarrage
- Position cachée ajustée à -620px pour invisibilité totale
- Gestion dynamique du décalage selon la largeur
- Indicateur de redimensionnement rendu plus visible (3 barres CSS)
- Zone de clic élargie à 16px pour meilleure ergonomie
2025-09-04 16:36:04 -04:00
Pierre Marx d66af7d99d feat: Ajout du redimensionnement manuel du panneau de notes
- Poignée de redimensionnement avec indicateur visuel permanent (3 points)
- Largeur ajustable entre 280px et 600px
- Sauvegarde automatique de la largeur préférée
- Optimisations avec requestAnimationFrame pour la fluidité
- Overlay pendant le drag pour capturer tous les mouvements
- Indicateur change de couleur au survol (gris → violet)
2025-09-04 16:22:09 -04:00
Pierre Marx 29a4cfb410 feat: Refonte moderne du panneau de notes avec design latéral
- Transformation en panneau latéral droit au lieu du bas
- Header avec gradient violet et design moderne
- Ajout bouton Effacer et amélioration du placeholder
- Suppression du badge rouge de notification
- Animations fluides et redimensionnement automatique de la webview
- Interface plus cohérente et intuitive
2025-09-04 16:14:42 -04:00
Pierre Marx d6e3942e97 fix: Uniformisation des messages d'animation de connexion
- Messages identiques avec ou sans case Débloquer cochée
- Suppression de la différenciation visuelle entre les types de connexion
- Interface plus cohérente et prévisible pour l'utilisateur
- Expérience unifiée quel que soit le mode de connexion choisi
2025-09-04 16:02:20 -04:00
Pierre Marx 6eaef1ddda fix: Ajout de l'autofocus sur le champ code d'accès pour une saisie immédiate
- Focus automatique au lancement de l'application
- Focus automatique après déconnexion
- Plus besoin de cliquer avant de commencer à taper
- Amélioration significative de l'ergonomie de connexion
2025-09-04 15:59:32 -04:00
Pierre Marx ef3ee22991 feat: Ajout d'une animation de connexion avec spinner et feedback visuel
- Modal de progression pendant l'authentification SignalR
- Messages dynamiques selon le contexte (connexion normale ou forcée)
- Design cohérent avec la modal de déconnexion
- Transitions fluides entre les étapes du processus
- Amélioration de l'expérience utilisateur avec feedback continu
2025-09-04 15:56:38 -04:00
Pierre Marx c7bef13dcd feat: Animation de progression pendant la déconnexion (v1.2.7)
- Ajout d'un spinner circulaire animé pendant la déconnexion
- Rotation fluide avec design violet cohérent
- Textes dynamiques "Déconnexion en cours..." pendant le processus
- Boutons masqués pour éviter les doubles clics
- Transitions en fondu entre icône et spinner
- Délais optimisés pour une expérience fluide
2025-09-04 15:51:01 -04:00
Pierre Marx f0b407d4b8 feat: Tri alphabétique des onglets de centres (v1.2.6)
- Les onglets sont maintenant triés par ordre alphabétique du code
- Utilisation de localeCompare() avec locale français
- Gestion correcte des codes avec nombres (ACR2 avant ACR10)
- Ordre prévisible et cohérent indépendant du serveur
- Premier onglet sélectionné = premier alphabétiquement
2025-09-04 15:46:44 -04:00
Pierre Marx 6ad4cb9b54 fix: Sélection automatique du premier onglet à la connexion (v1.2.5)
- Correction de l'écran blanc après connexion
- Le premier planning s'affiche automatiquement
- Suppression du message "Sélectionnez un centre..."
- Expérience utilisateur plus fluide avec accès direct au contenu
- L'onglet du premier centre est marqué comme actif
2025-09-04 15:41:21 -04:00
Pierre Marx f511a9ad17 fix: Réinitialisation du formulaire de connexion après déconnexion (v1.2.4)
- Correction du bug où le bouton restait grisé "Reconnexion..."
- Nouvelle fonction resetLoginForm() pour nettoyer le formulaire
- Vide les champs code/mot de passe et décoche "Débloquer"
- Réactive le bouton avec le texte "Se connecter"
- Formulaire propre et fonctionnel après déconnexion
- Préserve la sélection du terminal pour la commodité
2025-09-04 15:35:09 -04:00
Pierre Marx da20170bef feat: Modal de déconnexion personnalisée remplaçant le confirm natif (v1.2.3)
- Nouvelle modal moderne avec design élégant et animations
- Icône emoji 👋 avec gradient violet et animation pulse
- Textes en français avec titre et description
- Fond flou avec overlay sombre pour meilleur focus
- Trois méthodes de fermeture : bouton, clic externe, Escape
- Suppression du popup système Electron
2025-09-04 15:27:22 -04:00
Pierre Marx fb9430936c refactor: Suppression de la toolbar des webviews et ajout du bouton Rafraîchir dans le header (v1.2.2)
- Suppression complète de la barre d'outils des webviews
- Plus de boutons Précédent/Suivant et d'affichage d'URL
- Bouton Rafraîchir déplacé dans le header principal
- Animation de rotation lors du rafraîchissement
- Gain d'espace vertical supplémentaire (~40px)
- Code nettoyé avec suppression des fonctions inutilisées
2025-09-04 15:22:34 -04:00
Pierre Marx bcd56d5e1e refactor: Fusion du header et des onglets pour optimiser l'espace vertical (v1.2.1)
- Header et barre d'onglets combinés sur une seule ligne
- Gain de 10px en hauteur (60px au lieu de 70px)
- Logo et agent à gauche, onglets au centre, actions à droite
- Interface plus compacte avec éléments redimensionnés
- Plus d'espace vertical disponible pour les webviews
2025-09-04 15:18:08 -04:00
Pierre Marx 892d55a9f8 refactor: Refonte majeure de l'interface utilisateur (v1.2.0)
SUPPRESSIONS:
- Sidebar latérale gauche complètement retirée
- Statistiques du jour supprimées
- Bouton et modal de simulation d'appel supprimés
- Scrollbars visibles masquées

AJOUTS:
- Zone de notes dynamique avec toggle via bouton 📝
- Sauvegarde des préférences dans localStorage
- Nouveau design moderne avec styles-modern.css

AMÉLIORATIONS:
- Interface épurée maximisant l'espace pour les webviews
- Onglets style Material Design avec animations
- Meilleure gestion de l'espace avec calc() CSS
- Code HTML et JavaScript nettoyé et simplifié
2025-09-04 15:12:31 -04:00
Pierre Marx 2bf8c3605a feat: Affichage du code client dans les onglets des plannings
Les titres des onglets affichent maintenant le code client (centre.id)
au lieu du nom de la file d'attente (centre.nom) pour une identification
plus directe et claire du client concerné.
2025-09-04 14:53:19 -04:00
Pierre Marx a8f167b7c5 refactor: simplification des libellés et titre
- Renommage checkbox "Déconnexion" → "Débloquer" pour plus de clarté
- Simplification du titre de l'application en "SimpleConnect"
- Mise à jour du changelog v1.1.2
2025-09-04 14:41:44 -04:00
Pierre MarxandAssistant 50c46438c7 docs: mise à jour du changelog v1.1.1
Ajout de la documentation pour la nouvelle fonctionnalité de déconnexion forcée:
- Option checkbox sur l'écran de connexion
- Gestion des sessions bloquées après crash
- Design moderne et accessible de l'interface

Co-Authored-By: Assistant <noreply@anthropic.com>
2025-09-04 14:37:14 -04:00
Pierre Marx ec0ea73e35 checkbox clickable et modification des textes 2025-09-04 14:34:54 -04:00
Pierre Marx 5bfdb286db textbox (non clickable) 2025-09-04 14:32:16 -04:00
Pierre MarxandClaude 1c4d304a54 docs: mise à jour du changelog v1.1.0
- Ajout de l'authentification SignalR réelle
- Documentation des événements IPBX et basculement automatique
- Suppression de l'authentification locale simulée

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-04 14:20:31 -04:00
Pierre Marx 95cd40fe42 docs workflow deconnexion forcée 2025-09-04 14:20:06 -04:00
Pierre Marx 6069a7238b connexion avec signalR 2025-09-04 14:18:36 -04:00
Pierre Marx d6f89ed686 refactor: nettoyage du code et correction de bugs mineurs
- Correction de l'ouverture des DevTools uniquement en mode développement
- Fix de la détection du mode développement pour le simulateur CTI via IPC
- Suppression de la boucle de retry infinie pour Choices.js
- Suppression du code mort (fonction updateSignalRStatus vide)
- Amélioration de la gestion d'erreur avec fallback natif pour Choices.js
2025-09-04 13:43:26 -04:00
Pierre Marx c7c48360fd choices.js pour la liste déroulante 2025-09-04 13:37:31 -04:00
Pierre Marx a22344d664 liste déroulante postes téléphoniques 2025-09-04 13:06:17 -04:00
Pierre Marx ee038cedd8 modif doc 2025-09-04 12:33:46 -04:00
Pierre Marx 3f21bc0465 Statut de connexion au serveur 2025-09-04 12:32:09 -04:00
Pierre Marx e6eaba3018 docs signalR 2025-09-04 11:55:08 -04:00
Pierre Marx 7e34c471de Version initiale 2025-09-04 11:43:50 -04:00
19 changed files with 4764 additions and 3 deletions
+1
View File
@@ -0,0 +1 @@
Que fait ce code ? Je veux que tu te prépares à discuter de la manière dont fonctionne notre code. I want you to ultrathink. Je veux que tu sois capable de répondre à des questions de la part de mes employés sur les différentes parties du système. N'écris pas de notes mais contente toi de répondre aux questions dans le chat. Récupère les docs pertinentes avec context7 pour être sûr d'avoir la documentation à jour. Regarde notamment s'ils existent les fichiers de documentation comme README.MD, CLAUDE.MD, changelog.md et todo.md ainsi que les derniers commits et branches crées pour savoir ce sur quoi on est en train de travailler.
+43
View File
@@ -0,0 +1,43 @@
# Dependencies
node_modules/
package-lock.json
yarn.lock
# Build output
dist/
build/
out/
# Electron
.electron/
# Notes and call history
notes/
call_history.json
# OS files
.DS_Store
Thumbs.db
desktop.ini
# IDE
.vscode/
.idea/
*.sublime-project
*.sublime-workspace
# Logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Environment variables
.env
.env.local
.env.*.local
# Temporary files
*.tmp
*.temp
*~
+53
View File
@@ -0,0 +1,53 @@
# SimpleClient
Application desktop de télésecrétariat pour les postes RDVPREM.
## Stack
- **Electron 28** + **Socket.IO** (socket.io-client 4.8.1) + **Choices.js** 11.1.0
- HTML/CSS/JavaScript natif (pas de framework frontend)
## Structure
```
├── main.js # Process principal — Socket.IO, IPC, fenêtres
├── socketio-adapter.js # Adaptateur Socket.IO (connect/logoff/disconnect)
├── renderer.js # Process renderer — UI, webviews
├── index.html # Structure HTML
├── styles-modern.css # Styles CSS
├── config.json # Config Socket.IO (serverUrl, serviceProvider)
└── notes/ # Stockage notes agents (notes_{agentId}.json)
```
## Commandes
```bash
bun run dev # Mode dev avec DevTools
bun start # Production
bun run build # Build toutes plateformes
bun run build:linux # Build Linux (AppImage, .deb, .rpm)
bun run build:mac # Build macOS (.dmg, .app)
```
## Tests
```bash
bun test # 8 tests unitaires socketio-adapter
```
- `socketio-adapter.js` accepte un socket factory en 2e param (injection pour tests)
- Fake socket avec EventEmitter minimal dans `socketio-adapter.test.js`
## Points d'attention
- **Pas d'emojis** dans l'UI — icônes SVG inline (compatibilité Linux)
- **Sessions webview isolées** : partition Electron unique par centre, auto-connexion via preload script
- **Socket.IO reconnexion** : illimitée (2s→10s backoff)
- **IPC principal** : `login-agent`, `get-terminal-list`, `server-status`, `switch-to-center`, `release-center`
- **Protocole serveur** : auth au handshake, events `login_ok`/`login_error`/`call_event`/`logout`→`logout_ok` (champs snake_case : `event_code`, `queue_name`, `terminal`)
- **Terminaux** : REST `GET /terminals?provider=RDVPREM` (pas Socket.IO)
- **Logs** : `~/.simpleconnect-ng/socketio.log`
- **Notes** : sauvegarde auto après 2s d'inactivité, 50 versions, sync localStorage + fichier
## Workflow de développement
Voir [.claude/commands/dev.md](.claude/commands/dev.md) pour le guide complet.
-3
View File
@@ -1,3 +0,0 @@
# SimpleClient-releases
SimpleConnect Client - Releases
+57
View File
@@ -0,0 +1,57 @@
#!/bin/bash
# Script de build pour Linux AMD64 depuis Mac ARM64
# Ce script utilise electron-builder pour créer un package Linux x64
echo "🚀 Build SimpleConnect pour Linux AMD64"
echo "========================================="
# Vérifier que nous sommes dans le bon répertoire
if [ ! -f "package.json" ]; then
echo "❌ Erreur: package.json non trouvé. Exécutez ce script depuis la racine du projet."
exit 1
fi
# Nettoyer les builds précédents
echo "🧹 Nettoyage des builds précédents..."
rm -rf dist/
# Installer les dépendances si nécessaire
echo "📦 Vérification des dépendances..."
if [ ! -d "node_modules" ]; then
echo "Installation des dépendances..."
npm install
fi
# Configuration pour le cross-compilation
export ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES=true
# Build pour Linux x64
echo "🔨 Build en cours pour Linux x64..."
echo "Note: Le build cross-platform peut prendre quelques minutes..."
# Utiliser npx pour garantir l'utilisation de electron-builder local
npx electron-builder --linux --x64
# Vérifier le succès du build
if [ $? -eq 0 ]; then
echo ""
echo "✅ Build terminé avec succès!"
echo ""
echo "📦 Fichiers générés dans ./dist/ :"
echo "========================================="
ls -lh dist/*.AppImage 2>/dev/null && echo " ✓ AppImage (format universel)"
ls -lh dist/*.deb 2>/dev/null && echo " ✓ Package Debian/Ubuntu"
ls -lh dist/*.rpm 2>/dev/null && echo " ✓ Package RedHat/Fedora"
echo ""
echo "📋 Instructions de déploiement:"
echo "1. Copier le fichier approprié sur la machine Linux cible"
echo "2. Pour AppImage: chmod +x SimpleConnect-*.AppImage && ./SimpleConnect-*.AppImage"
echo "3. Pour .deb: sudo dpkg -i simpleconnect-electron_*.deb"
echo "4. Pour .rpm: sudo rpm -i simpleconnect-electron-*.rpm"
else
echo ""
echo "❌ Erreur lors du build"
echo "Vérifiez les messages d'erreur ci-dessus"
exit 1
fi
+660
View File
@@ -0,0 +1,660 @@
{
"lockfileVersion": 1,
"configVersion": 1,
"workspaces": {
"": {
"name": "simpleconnect-electron",
"dependencies": {
"choices.js": "^11.1.0",
"socket.io-client": "^4.8.1",
},
"devDependencies": {
"electron": "^28.0.0",
"electron-builder": "^24.9.1",
},
},
},
"packages": {
"7zip-bin": ["7zip-bin@5.2.0", "", {}, "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A=="],
"@develar/schema-utils": ["@develar/schema-utils@2.6.5", "", { "dependencies": { "ajv": "^6.12.0", "ajv-keywords": "^3.4.1" } }, "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig=="],
"@electron/asar": ["@electron/asar@3.4.1", "", { "dependencies": { "commander": "^5.0.0", "glob": "^7.1.6", "minimatch": "^3.0.4" }, "bin": { "asar": "bin/asar.js" } }, "sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA=="],
"@electron/get": ["@electron/get@2.0.3", "", { "dependencies": { "debug": "^4.1.1", "env-paths": "^2.2.0", "fs-extra": "^8.1.0", "got": "^11.8.5", "progress": "^2.0.3", "semver": "^6.2.0", "sumchecker": "^3.0.1" }, "optionalDependencies": { "global-agent": "^3.0.0" } }, "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ=="],
"@electron/notarize": ["@electron/notarize@2.2.1", "", { "dependencies": { "debug": "^4.1.1", "fs-extra": "^9.0.1", "promise-retry": "^2.0.1" } }, "sha512-aL+bFMIkpR0cmmj5Zgy0LMKEpgy43/hw5zadEArgmAMWWlKc5buwFvFT9G/o/YJkvXAJm5q3iuTuLaiaXW39sg=="],
"@electron/osx-sign": ["@electron/osx-sign@1.0.5", "", { "dependencies": { "compare-version": "^0.1.2", "debug": "^4.3.4", "fs-extra": "^10.0.0", "isbinaryfile": "^4.0.8", "minimist": "^1.2.6", "plist": "^3.0.5" }, "bin": { "electron-osx-flat": "bin/electron-osx-flat.js", "electron-osx-sign": "bin/electron-osx-sign.js" } }, "sha512-k9ZzUQtamSoweGQDV2jILiRIHUu7lYlJ3c6IEmjv1hC17rclE+eb9U+f6UFlOOETo0JzY1HNlXy4YOlCvl+Lww=="],
"@electron/universal": ["@electron/universal@1.5.1", "", { "dependencies": { "@electron/asar": "^3.2.1", "@malept/cross-spawn-promise": "^1.1.0", "debug": "^4.3.1", "dir-compare": "^3.0.0", "fs-extra": "^9.0.1", "minimatch": "^3.0.4", "plist": "^3.0.4" } }, "sha512-kbgXxyEauPJiQQUNG2VgUeyfQNFk6hBF11ISN2PNI6agUgPl55pv4eQmaqHzTAzchBvqZ2tQuRVaPStGf0mxGw=="],
"@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="],
"@malept/cross-spawn-promise": ["@malept/cross-spawn-promise@1.1.1", "", { "dependencies": { "cross-spawn": "^7.0.1" } }, "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ=="],
"@malept/flatpak-bundler": ["@malept/flatpak-bundler@0.4.0", "", { "dependencies": { "debug": "^4.1.1", "fs-extra": "^9.0.0", "lodash": "^4.17.15", "tmp-promise": "^3.0.2" } }, "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q=="],
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
"@sindresorhus/is": ["@sindresorhus/is@4.6.0", "", {}, "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw=="],
"@socket.io/component-emitter": ["@socket.io/component-emitter@3.1.2", "", {}, "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="],
"@szmarczak/http-timer": ["@szmarczak/http-timer@4.0.6", "", { "dependencies": { "defer-to-connect": "^2.0.0" } }, "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w=="],
"@tootallnate/once": ["@tootallnate/once@2.0.0", "", {}, "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A=="],
"@types/cacheable-request": ["@types/cacheable-request@6.0.3", "", { "dependencies": { "@types/http-cache-semantics": "*", "@types/keyv": "^3.1.4", "@types/node": "*", "@types/responselike": "^1.0.0" } }, "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw=="],
"@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
"@types/fs-extra": ["@types/fs-extra@9.0.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA=="],
"@types/http-cache-semantics": ["@types/http-cache-semantics@4.2.0", "", {}, "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q=="],
"@types/keyv": ["@types/keyv@3.1.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg=="],
"@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="],
"@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="],
"@types/plist": ["@types/plist@3.0.5", "", { "dependencies": { "@types/node": "*", "xmlbuilder": ">=11.0.1" } }, "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA=="],
"@types/responselike": ["@types/responselike@1.0.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw=="],
"@types/verror": ["@types/verror@1.10.11", "", {}, "sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg=="],
"@types/yauzl": ["@types/yauzl@2.10.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q=="],
"@xmldom/xmldom": ["@xmldom/xmldom@0.8.11", "", {}, "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw=="],
"agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="],
"ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="],
"ajv-keywords": ["ajv-keywords@3.5.2", "", { "peerDependencies": { "ajv": "^6.9.1" } }, "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ=="],
"ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
"app-builder-bin": ["app-builder-bin@4.0.0", "", {}, "sha512-xwdG0FJPQMe0M0UA4Tz0zEB8rBJTRA5a476ZawAqiBkMv16GRK5xpXThOjMaEOFnZ6zabejjG4J3da0SXG63KA=="],
"app-builder-lib": ["app-builder-lib@24.13.3", "", { "dependencies": { "@develar/schema-utils": "~2.6.5", "@electron/notarize": "2.2.1", "@electron/osx-sign": "1.0.5", "@electron/universal": "1.5.1", "@malept/flatpak-bundler": "^0.4.0", "@types/fs-extra": "9.0.13", "async-exit-hook": "^2.0.1", "bluebird-lst": "^1.0.9", "builder-util": "24.13.1", "builder-util-runtime": "9.2.4", "chromium-pickle-js": "^0.2.0", "debug": "^4.3.4", "ejs": "^3.1.8", "electron-publish": "24.13.1", "form-data": "^4.0.0", "fs-extra": "^10.1.0", "hosted-git-info": "^4.1.0", "is-ci": "^3.0.0", "isbinaryfile": "^5.0.0", "js-yaml": "^4.1.0", "lazy-val": "^1.0.5", "minimatch": "^5.1.1", "read-config-file": "6.3.2", "sanitize-filename": "^1.6.3", "semver": "^7.3.8", "tar": "^6.1.12", "temp-file": "^3.4.0" }, "peerDependencies": { "dmg-builder": "24.13.3", "electron-builder-squirrel-windows": "24.13.3" } }, "sha512-FAzX6IBit2POXYGnTCT8YHFO/lr5AapAII6zzhQO3Rw4cEDOgK+t1xhLc5tNcKlicTHlo9zxIwnYCX9X2DLkig=="],
"archiver": ["archiver@5.3.2", "", { "dependencies": { "archiver-utils": "^2.1.0", "async": "^3.2.4", "buffer-crc32": "^0.2.1", "readable-stream": "^3.6.0", "readdir-glob": "^1.1.2", "tar-stream": "^2.2.0", "zip-stream": "^4.1.0" } }, "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw=="],
"archiver-utils": ["archiver-utils@2.1.0", "", { "dependencies": { "glob": "^7.1.4", "graceful-fs": "^4.2.0", "lazystream": "^1.0.0", "lodash.defaults": "^4.2.0", "lodash.difference": "^4.5.0", "lodash.flatten": "^4.4.0", "lodash.isplainobject": "^4.0.6", "lodash.union": "^4.6.0", "normalize-path": "^3.0.0", "readable-stream": "^2.0.0" } }, "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw=="],
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
"assert-plus": ["assert-plus@1.0.0", "", {}, "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw=="],
"astral-regex": ["astral-regex@2.0.0", "", {}, "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ=="],
"async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="],
"async-exit-hook": ["async-exit-hook@2.0.1", "", {}, "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw=="],
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
"at-least-node": ["at-least-node@1.0.0", "", {}, "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg=="],
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
"bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="],
"bluebird": ["bluebird@3.7.2", "", {}, "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="],
"bluebird-lst": ["bluebird-lst@1.0.9", "", { "dependencies": { "bluebird": "^3.5.5" } }, "sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw=="],
"boolean": ["boolean@3.2.0", "", {}, "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw=="],
"brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
"buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
"buffer-crc32": ["buffer-crc32@0.2.13", "", {}, "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="],
"buffer-equal": ["buffer-equal@1.0.1", "", {}, "sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg=="],
"buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
"builder-util": ["builder-util@24.13.1", "", { "dependencies": { "7zip-bin": "~5.2.0", "@types/debug": "^4.1.6", "app-builder-bin": "4.0.0", "bluebird-lst": "^1.0.9", "builder-util-runtime": "9.2.4", "chalk": "^4.1.2", "cross-spawn": "^7.0.3", "debug": "^4.3.4", "fs-extra": "^10.1.0", "http-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.1", "is-ci": "^3.0.0", "js-yaml": "^4.1.0", "source-map-support": "^0.5.19", "stat-mode": "^1.0.0", "temp-file": "^3.4.0" } }, "sha512-NhbCSIntruNDTOVI9fdXz0dihaqX2YuE1D6zZMrwiErzH4ELZHE6mdiB40wEgZNprDia+FghRFgKoAqMZRRjSA=="],
"builder-util-runtime": ["builder-util-runtime@9.2.4", "", { "dependencies": { "debug": "^4.3.4", "sax": "^1.2.4" } }, "sha512-upp+biKpN/XZMLim7aguUyW8s0FUpDvOtK6sbanMFDAMBzpHDqdhgVYm6zc9HJ6nWo7u2Lxk60i2M6Jd3aiNrA=="],
"cacheable-lookup": ["cacheable-lookup@5.0.4", "", {}, "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA=="],
"cacheable-request": ["cacheable-request@7.0.4", "", { "dependencies": { "clone-response": "^1.0.2", "get-stream": "^5.1.0", "http-cache-semantics": "^4.0.0", "keyv": "^4.0.0", "lowercase-keys": "^2.0.0", "normalize-url": "^6.0.1", "responselike": "^2.0.0" } }, "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg=="],
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
"choices.js": ["choices.js@11.2.1", "", { "dependencies": { "fuse.js": "^7.0.0" } }, "sha512-SyA0FRr0+UHOjKO/9oR0/hVYRgk3v5C9ZD+CLnQfLFR7ZSiNb/rQeAUOsqXaPTk/552X5dtvRaDiG94Ga7G2Gg=="],
"chownr": ["chownr@2.0.0", "", {}, "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="],
"chromium-pickle-js": ["chromium-pickle-js@0.2.0", "", {}, "sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw=="],
"ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="],
"cli-truncate": ["cli-truncate@2.1.0", "", { "dependencies": { "slice-ansi": "^3.0.0", "string-width": "^4.2.0" } }, "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg=="],
"cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
"clone-response": ["clone-response@1.0.3", "", { "dependencies": { "mimic-response": "^1.0.0" } }, "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA=="],
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
"combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
"commander": ["commander@5.1.0", "", {}, "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg=="],
"compare-version": ["compare-version@0.1.2", "", {}, "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A=="],
"compress-commons": ["compress-commons@4.1.2", "", { "dependencies": { "buffer-crc32": "^0.2.13", "crc32-stream": "^4.0.2", "normalize-path": "^3.0.0", "readable-stream": "^3.6.0" } }, "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg=="],
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
"config-file-ts": ["config-file-ts@0.2.6", "", { "dependencies": { "glob": "^10.3.10", "typescript": "^5.3.3" } }, "sha512-6boGVaglwblBgJqGyxm4+xCmEGcWgnWHSWHY5jad58awQhB6gftq0G8HbzU39YqCIYHMLAiL1yjwiZ36m/CL8w=="],
"core-util-is": ["core-util-is@1.0.2", "", {}, "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="],
"crc": ["crc@3.8.0", "", { "dependencies": { "buffer": "^5.1.0" } }, "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ=="],
"crc-32": ["crc-32@1.2.2", "", { "bin": { "crc32": "bin/crc32.njs" } }, "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ=="],
"crc32-stream": ["crc32-stream@4.0.3", "", { "dependencies": { "crc-32": "^1.2.0", "readable-stream": "^3.4.0" } }, "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw=="],
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
"decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="],
"defer-to-connect": ["defer-to-connect@2.0.1", "", {}, "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg=="],
"define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="],
"define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="],
"delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
"detect-node": ["detect-node@2.1.0", "", {}, "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g=="],
"dir-compare": ["dir-compare@3.3.0", "", { "dependencies": { "buffer-equal": "^1.0.0", "minimatch": "^3.0.4" } }, "sha512-J7/et3WlGUCxjdnD3HAAzQ6nsnc0WL6DD7WcwJb7c39iH1+AWfg+9OqzJNaI6PkBwBvm1mhZNL9iY/nRiZXlPg=="],
"dmg-builder": ["dmg-builder@24.13.3", "", { "dependencies": { "app-builder-lib": "24.13.3", "builder-util": "24.13.1", "builder-util-runtime": "9.2.4", "fs-extra": "^10.1.0", "iconv-lite": "^0.6.2", "js-yaml": "^4.1.0" }, "optionalDependencies": { "dmg-license": "^1.0.11" } }, "sha512-rcJUkMfnJpfCboZoOOPf4L29TRtEieHNOeAbYPWPxlaBw/Z1RKrRA86dOI9rwaI4tQSc/RD82zTNHprfUHXsoQ=="],
"dmg-license": ["dmg-license@1.0.11", "", { "dependencies": { "@types/plist": "^3.0.1", "@types/verror": "^1.10.3", "ajv": "^6.10.0", "crc": "^3.8.0", "iconv-corefoundation": "^1.1.7", "plist": "^3.0.4", "smart-buffer": "^4.0.2", "verror": "^1.10.0" }, "os": "darwin", "bin": { "dmg-license": "bin/dmg-license.js" } }, "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q=="],
"dotenv": ["dotenv@9.0.2", "", {}, "sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg=="],
"dotenv-expand": ["dotenv-expand@5.1.0", "", {}, "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA=="],
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
"eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],
"ejs": ["ejs@3.1.10", "", { "dependencies": { "jake": "^10.8.5" }, "bin": { "ejs": "bin/cli.js" } }, "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA=="],
"electron": ["electron@28.3.3", "", { "dependencies": { "@electron/get": "^2.0.0", "@types/node": "^18.11.18", "extract-zip": "^2.0.1" }, "bin": { "electron": "cli.js" } }, "sha512-ObKMLSPNhomtCOBAxFS8P2DW/4umkh72ouZUlUKzXGtYuPzgr1SYhskhFWgzAsPtUzhL2CzyV2sfbHcEW4CXqw=="],
"electron-builder": ["electron-builder@24.13.3", "", { "dependencies": { "app-builder-lib": "24.13.3", "builder-util": "24.13.1", "builder-util-runtime": "9.2.4", "chalk": "^4.1.2", "dmg-builder": "24.13.3", "fs-extra": "^10.1.0", "is-ci": "^3.0.0", "lazy-val": "^1.0.5", "read-config-file": "6.3.2", "simple-update-notifier": "2.0.0", "yargs": "^17.6.2" }, "bin": { "electron-builder": "cli.js", "install-app-deps": "install-app-deps.js" } }, "sha512-yZSgVHft5dNVlo31qmJAe4BVKQfFdwpRw7sFp1iQglDRCDD6r22zfRJuZlhtB5gp9FHUxCMEoWGq10SkCnMAIg=="],
"electron-builder-squirrel-windows": ["electron-builder-squirrel-windows@24.13.3", "", { "dependencies": { "app-builder-lib": "24.13.3", "archiver": "^5.3.1", "builder-util": "24.13.1", "fs-extra": "^10.1.0" } }, "sha512-oHkV0iogWfyK+ah9ZIvMDpei1m9ZRpdXcvde1wTpra2U8AFDNNpqJdnin5z+PM1GbQ5BoaKCWas2HSjtR0HwMg=="],
"electron-publish": ["electron-publish@24.13.1", "", { "dependencies": { "@types/fs-extra": "^9.0.11", "builder-util": "24.13.1", "builder-util-runtime": "9.2.4", "chalk": "^4.1.2", "fs-extra": "^10.1.0", "lazy-val": "^1.0.5", "mime": "^2.5.2" } }, "sha512-2ZgdEqJ8e9D17Hwp5LEq5mLQPjqU3lv/IALvgp+4W8VeNhryfGhYEQC/PgDPMrnWUp+l60Ou5SJLsu+k4mhQ8A=="],
"emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
"end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="],
"engine.io-client": ["engine.io-client@6.6.4", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1", "engine.io-parser": "~5.2.1", "ws": "~8.18.3", "xmlhttprequest-ssl": "~2.1.1" } }, "sha512-+kjUJnZGwzewFDw951CDWcwj35vMNf2fcj7xQWOctq1F2i1jkDdVvdFG9kM/BEChymCH36KgjnW0NsL58JYRxw=="],
"engine.io-parser": ["engine.io-parser@5.2.3", "", {}, "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q=="],
"env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="],
"err-code": ["err-code@2.0.3", "", {}, "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA=="],
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
"es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
"es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
"es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
"es6-error": ["es6-error@4.1.1", "", {}, "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg=="],
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
"extract-zip": ["extract-zip@2.0.1", "", { "dependencies": { "debug": "^4.1.1", "get-stream": "^5.1.0", "yauzl": "^2.10.0" }, "optionalDependencies": { "@types/yauzl": "^2.9.1" }, "bin": { "extract-zip": "cli.js" } }, "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg=="],
"extsprintf": ["extsprintf@1.4.1", "", {}, "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA=="],
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
"fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
"fd-slicer": ["fd-slicer@1.1.0", "", { "dependencies": { "pend": "~1.2.0" } }, "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g=="],
"filelist": ["filelist@1.0.6", "", { "dependencies": { "minimatch": "^5.0.1" } }, "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA=="],
"foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="],
"form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="],
"fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="],
"fs-extra": ["fs-extra@10.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ=="],
"fs-minipass": ["fs-minipass@2.1.0", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg=="],
"fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="],
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
"fuse.js": ["fuse.js@7.1.0", "", {}, "sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ=="],
"get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="],
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
"get-stream": ["get-stream@5.2.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA=="],
"glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="],
"global-agent": ["global-agent@3.0.0", "", { "dependencies": { "boolean": "^3.0.1", "es6-error": "^4.1.1", "matcher": "^3.0.0", "roarr": "^2.15.3", "semver": "^7.3.2", "serialize-error": "^7.0.1" } }, "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q=="],
"globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="],
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
"got": ["got@11.8.6", "", { "dependencies": { "@sindresorhus/is": "^4.0.0", "@szmarczak/http-timer": "^4.0.5", "@types/cacheable-request": "^6.0.1", "@types/responselike": "^1.0.0", "cacheable-lookup": "^5.0.3", "cacheable-request": "^7.0.2", "decompress-response": "^6.0.0", "http2-wrapper": "^1.0.0-beta.5.2", "lowercase-keys": "^2.0.0", "p-cancelable": "^2.0.0", "responselike": "^2.0.0" } }, "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g=="],
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
"has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="],
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
"has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
"hosted-git-info": ["hosted-git-info@4.1.0", "", { "dependencies": { "lru-cache": "^6.0.0" } }, "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA=="],
"http-cache-semantics": ["http-cache-semantics@4.2.0", "", {}, "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ=="],
"http-proxy-agent": ["http-proxy-agent@5.0.0", "", { "dependencies": { "@tootallnate/once": "2", "agent-base": "6", "debug": "4" } }, "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w=="],
"http2-wrapper": ["http2-wrapper@1.0.3", "", { "dependencies": { "quick-lru": "^5.1.1", "resolve-alpn": "^1.0.0" } }, "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg=="],
"https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="],
"iconv-corefoundation": ["iconv-corefoundation@1.1.7", "", { "dependencies": { "cli-truncate": "^2.1.0", "node-addon-api": "^1.6.3" }, "os": "darwin" }, "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ=="],
"iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
"inflight": ["inflight@1.0.6", "", { "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA=="],
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
"is-ci": ["is-ci@3.0.1", "", { "dependencies": { "ci-info": "^3.2.0" }, "bin": { "is-ci": "bin.js" } }, "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ=="],
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
"isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
"isbinaryfile": ["isbinaryfile@5.0.7", "", {}, "sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ=="],
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
"jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="],
"jake": ["jake@10.9.4", "", { "dependencies": { "async": "^3.2.6", "filelist": "^1.0.4", "picocolors": "^1.1.1" }, "bin": { "jake": "bin/cli.js" } }, "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA=="],
"js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
"json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
"json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
"json-stringify-safe": ["json-stringify-safe@5.0.1", "", {}, "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="],
"json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
"jsonfile": ["jsonfile@6.2.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg=="],
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
"lazy-val": ["lazy-val@1.0.5", "", {}, "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q=="],
"lazystream": ["lazystream@1.0.1", "", { "dependencies": { "readable-stream": "^2.0.5" } }, "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw=="],
"lodash": ["lodash@4.17.23", "", {}, "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w=="],
"lodash.defaults": ["lodash.defaults@4.2.0", "", {}, "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="],
"lodash.difference": ["lodash.difference@4.5.0", "", {}, "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA=="],
"lodash.flatten": ["lodash.flatten@4.4.0", "", {}, "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g=="],
"lodash.isplainobject": ["lodash.isplainobject@4.0.6", "", {}, "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="],
"lodash.union": ["lodash.union@4.6.0", "", {}, "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw=="],
"lowercase-keys": ["lowercase-keys@2.0.0", "", {}, "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="],
"lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="],
"matcher": ["matcher@3.0.0", "", { "dependencies": { "escape-string-regexp": "^4.0.0" } }, "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng=="],
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
"mime": ["mime@2.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg=="],
"mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
"mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
"mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="],
"minimatch": ["minimatch@5.1.9", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw=="],
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
"minipass": ["minipass@5.0.0", "", {}, "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ=="],
"minizlib": ["minizlib@2.1.2", "", { "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" } }, "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg=="],
"mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="],
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"node-addon-api": ["node-addon-api@1.7.2", "", {}, "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg=="],
"normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
"normalize-url": ["normalize-url@6.1.0", "", {}, "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A=="],
"object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="],
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
"p-cancelable": ["p-cancelable@2.1.1", "", {}, "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg=="],
"package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="],
"path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="],
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
"path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
"pend": ["pend@1.2.0", "", {}, "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="],
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
"plist": ["plist@3.1.0", "", { "dependencies": { "@xmldom/xmldom": "^0.8.8", "base64-js": "^1.5.1", "xmlbuilder": "^15.1.1" } }, "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ=="],
"process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="],
"progress": ["progress@2.0.3", "", {}, "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="],
"promise-retry": ["promise-retry@2.0.1", "", { "dependencies": { "err-code": "^2.0.2", "retry": "^0.12.0" } }, "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g=="],
"pump": ["pump@3.0.4", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA=="],
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
"quick-lru": ["quick-lru@5.1.1", "", {}, "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="],
"read-config-file": ["read-config-file@6.3.2", "", { "dependencies": { "config-file-ts": "^0.2.4", "dotenv": "^9.0.2", "dotenv-expand": "^5.1.0", "js-yaml": "^4.1.0", "json5": "^2.2.0", "lazy-val": "^1.0.4" } }, "sha512-M80lpCjnE6Wt6zb98DoW8WHR09nzMSpu8XHtPkiTHrJ5Az9CybfeQhTJ8D7saeBHpGhLPIVyA8lcL6ZmdKwY6Q=="],
"readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
"readdir-glob": ["readdir-glob@1.1.3", "", { "dependencies": { "minimatch": "^5.1.0" } }, "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA=="],
"require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="],
"resolve-alpn": ["resolve-alpn@1.2.1", "", {}, "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g=="],
"responselike": ["responselike@2.0.1", "", { "dependencies": { "lowercase-keys": "^2.0.0" } }, "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw=="],
"retry": ["retry@0.12.0", "", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="],
"roarr": ["roarr@2.15.4", "", { "dependencies": { "boolean": "^3.0.1", "detect-node": "^2.0.4", "globalthis": "^1.0.1", "json-stringify-safe": "^5.0.1", "semver-compare": "^1.0.0", "sprintf-js": "^1.1.2" } }, "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A=="],
"safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
"sanitize-filename": ["sanitize-filename@1.6.3", "", { "dependencies": { "truncate-utf8-bytes": "^1.0.0" } }, "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg=="],
"sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
"semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
"semver-compare": ["semver-compare@1.0.0", "", {}, "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow=="],
"serialize-error": ["serialize-error@7.0.1", "", { "dependencies": { "type-fest": "^0.13.1" } }, "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw=="],
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
"signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
"simple-update-notifier": ["simple-update-notifier@2.0.0", "", { "dependencies": { "semver": "^7.5.3" } }, "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w=="],
"slice-ansi": ["slice-ansi@3.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", "is-fullwidth-code-point": "^3.0.0" } }, "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ=="],
"smart-buffer": ["smart-buffer@4.2.0", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="],
"socket.io-client": ["socket.io-client@4.8.3", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1", "engine.io-client": "~6.6.1", "socket.io-parser": "~4.2.4" } }, "sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g=="],
"socket.io-parser": ["socket.io-parser@4.2.6", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1" } }, "sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg=="],
"source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
"source-map-support": ["source-map-support@0.5.21", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="],
"sprintf-js": ["sprintf-js@1.1.3", "", {}, "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA=="],
"stat-mode": ["stat-mode@1.0.0", "", {}, "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg=="],
"string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
"string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
"string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
"strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
"strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
"sumchecker": ["sumchecker@3.0.1", "", { "dependencies": { "debug": "^4.1.0" } }, "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg=="],
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
"tar": ["tar@6.2.1", "", { "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" } }, "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A=="],
"tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="],
"temp-file": ["temp-file@3.4.0", "", { "dependencies": { "async-exit-hook": "^2.0.1", "fs-extra": "^10.0.0" } }, "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg=="],
"tmp": ["tmp@0.2.5", "", {}, "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow=="],
"tmp-promise": ["tmp-promise@3.0.3", "", { "dependencies": { "tmp": "^0.2.0" } }, "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ=="],
"truncate-utf8-bytes": ["truncate-utf8-bytes@1.0.2", "", { "dependencies": { "utf8-byte-length": "^1.0.1" } }, "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ=="],
"type-fest": ["type-fest@0.13.1", "", {}, "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg=="],
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
"undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
"universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="],
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
"utf8-byte-length": ["utf8-byte-length@1.0.5", "", {}, "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA=="],
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
"verror": ["verror@1.10.1", "", { "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", "extsprintf": "^1.2.0" } }, "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg=="],
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
"wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
"wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
"ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
"xmlbuilder": ["xmlbuilder@15.1.1", "", {}, "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg=="],
"xmlhttprequest-ssl": ["xmlhttprequest-ssl@2.1.2", "", {}, "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ=="],
"y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
"yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
"yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
"yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
"yauzl": ["yauzl@2.10.0", "", { "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } }, "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g=="],
"zip-stream": ["zip-stream@4.1.1", "", { "dependencies": { "archiver-utils": "^3.0.4", "compress-commons": "^4.1.2", "readable-stream": "^3.6.0" } }, "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ=="],
"@electron/asar/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
"@electron/asar/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="],
"@electron/get/fs-extra": ["fs-extra@8.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="],
"@electron/notarize/fs-extra": ["fs-extra@9.1.0", "", { "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ=="],
"@electron/osx-sign/isbinaryfile": ["isbinaryfile@4.0.10", "", {}, "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw=="],
"@electron/universal/fs-extra": ["fs-extra@9.1.0", "", { "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ=="],
"@electron/universal/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="],
"@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
"@isaacs/cliui/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="],
"@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
"@malept/flatpak-bundler/fs-extra": ["fs-extra@9.1.0", "", { "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ=="],
"@types/cacheable-request/@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="],
"@types/fs-extra/@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="],
"@types/keyv/@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="],
"@types/plist/@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="],
"@types/responselike/@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="],
"@types/yauzl/@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="],
"app-builder-lib/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
"archiver-utils/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
"archiver-utils/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
"clone-response/mimic-response": ["mimic-response@1.0.1", "", {}, "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="],
"dir-compare/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="],
"fs-minipass/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
"glob/minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="],
"glob/minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="],
"global-agent/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
"lazystream/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
"minizlib/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
"path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
"path-scurry/minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="],
"simple-update-notifier/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
"string_decoder/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
"zip-stream/archiver-utils": ["archiver-utils@3.0.4", "", { "dependencies": { "glob": "^7.2.3", "graceful-fs": "^4.2.0", "lazystream": "^1.0.0", "lodash.defaults": "^4.2.0", "lodash.difference": "^4.5.0", "lodash.flatten": "^4.4.0", "lodash.isplainobject": "^4.0.6", "lodash.union": "^4.6.0", "normalize-path": "^3.0.0", "readable-stream": "^3.6.0" } }, "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw=="],
"@electron/asar/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
"@electron/get/fs-extra/jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="],
"@electron/get/fs-extra/universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="],
"@electron/universal/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
"@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
"@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
"@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
"@types/cacheable-request/@types/node/undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
"@types/fs-extra/@types/node/undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
"@types/keyv/@types/node/undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
"@types/plist/@types/node/undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
"@types/responselike/@types/node/undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
"@types/yauzl/@types/node/undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
"archiver-utils/glob/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="],
"archiver-utils/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
"dir-compare/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
"lazystream/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
"zip-stream/archiver-utils/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
"archiver-utils/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
"zip-stream/archiver-utils/glob/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="],
"zip-stream/archiver-utils/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
}
}
+1
View File
File diff suppressed because one or more lines are too long
+2
View File
File diff suppressed because one or more lines are too long
+6
View File
@@ -0,0 +1,6 @@
{
"socketio": {
"serverUrl": "http://10.90.20.201:8004",
"serviceProvider": "RDVPREM"
}
}
+166
View File
@@ -0,0 +1,166 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>SimpleConnect</title>
<link rel="stylesheet" href="choices.min.css" />
<link rel="stylesheet" href="styles-modern.css" />
</head>
<body>
<!-- Page de connexion -->
<div id="loginPage" class="page active">
<div class="login-container">
<h1>SimpleConnect</h1>
<div class="app-version-login" id="appVersionLogin"></div>
<div class="server-status">
<span class="server-indicator" id="serverIndicator"></span>
<span class="server-text" id="serverText"
>Connexion au serveur...</span
>
</div>
<h2>Connexion Agent</h2>
<form id="loginForm">
<input
type="text"
id="accessCode"
placeholder="Code d'accès"
required
autofocus
/>
<input
type="password"
id="password"
placeholder="Mot de passe"
required
/>
<select id="terminal" required disabled>
<option value="">En attente du serveur...</option>
</select>
<button type="submit" disabled>Se connecter</button>
<button type="button" id="quitLoginBtn" class="btn-quit">Quitter</button>
<div id="loginError" class="error-message"></div>
</form>
</div>
</div>
<!-- Page principale -->
<div id="mainPage" class="page">
<!-- Zone d'alerte appel entrant -->
<div id="incomingCallAlert" class="incoming-call-alert">
<div class="call-icon">📞</div>
<div class="call-info">
<div class="call-title">APPEL ENTRANT</div>
<div class="call-center" id="callCenterName"></div>
<div class="call-patient" id="callPatientInfo"></div>
</div>
<button id="acceptCallBtn" class="btn-accept">Prendre l'appel</button>
</div>
<!-- Header et onglets combinés -->
<header class="header-with-tabs">
<div class="header-left">
<h1>SimpleConnect <span id="appVersion" class="app-version"></span></h1>
<span id="agentName" class="agent-name"></span>
</div>
<!-- Onglets des centres -->
<div class="tabs" id="centerTabs"></div>
<div class="header-right">
<div id="callStatus" class="call-status">
<span class="status-indicator" id="statusIndicator"></span>
<span id="statusText">Disponible</span>
</div>
<button id="refreshBtn" class="btn-icon" title="Rafraîchir la page">
<svg class="icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M23 4v6h-6"></path>
<path d="M1 20v-6h6"></path>
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path>
</svg>
</button>
<button id="toggleNotesBtn" class="btn-icon" title="Afficher/Masquer les notes">
<svg class="icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
<polyline points="14 2 14 8 20 8"></polyline>
<line x1="16" y1="13" x2="8" y2="13"></line>
<line x1="16" y1="17" x2="8" y2="17"></line>
<polyline points="10 9 9 9 8 9"></polyline>
</svg>
</button>
<button id="logoutBtn" class="btn-secondary">Quitter</button>
</div>
</header>
<!-- Conteneur principal -->
<div class="main-container">
<!-- Zone principale avec les webviews -->
<main class="content">
<!-- Container des webviews -->
<div id="webviewContainer" class="webview-container">
</div>
<!-- Zone de notes rapides - Panneau latéral -->
<div id="notesSection" class="notes-section">
<div class="notes-resize-handle" id="notesResizeHandle"></div>
<div class="notes-header">
<h4>Notes rapides</h4>
<button id="closeNotesBtn" class="btn-close-notes" title="Fermer">×</button>
</div>
<textarea
id="quickNotes"
placeholder="Prenez des notes ici pendant vos appels...
• Informations patient
• Motif de l'appel
• Actions à effectuer
• Remarques importantes"
></textarea>
<div class="notes-footer">
<button id="clearNotesBtn" class="btn-small btn-clear">Effacer</button>
<button id="saveNotesBtn" class="btn-small">Sauvegarder</button>
</div>
</div>
</main>
</div>
</div>
<!-- Modal de confirmation de déconnexion -->
<div id="logoutModal" class="modal-overlay">
<div class="logout-modal">
<div class="logout-modal-icon" id="logoutIcon">
<span>👋</span>
</div>
<div class="logout-spinner" id="logoutSpinner" style="display: none;">
<div class="spinner-ring"></div>
</div>
<h2 id="logoutTitle">Déconnexion</h2>
<p id="logoutText">Êtes-vous sûr de vouloir vous déconnecter ?</p>
<p class="logout-modal-subtitle" id="logoutSubtitle">Votre session sera fermée et vous devrez vous reconnecter.</p>
<div class="logout-modal-buttons" id="logoutButtons">
<button id="cancelLogoutBtn" class="btn-modal-cancel">Annuler</button>
<button id="confirmLogoutBtn" class="btn-modal-confirm">Se déconnecter</button>
</div>
</div>
</div>
<!-- Modal de progression de connexion -->
<div id="loginModal" class="modal-overlay">
<div class="login-progress-modal">
<div class="login-spinner">
<div class="spinner-ring"></div>
</div>
<h2 id="loginTitle">Connexion en cours...</h2>
<p id="loginText">Authentification auprès du serveur</p>
<p class="login-modal-subtitle" id="loginSubtitle">Veuillez patienter...</p>
</div>
</div>
<!-- Scripts -->
<script src="choices.min.js"></script>
<script src="renderer.js"></script>
</body>
</html>
+678
View File
@@ -0,0 +1,678 @@
const { app, BrowserWindow, ipcMain, session, net } = require('electron');
const path = require('path');
const fs = require('fs');
const os = require('os');
const SocketIOAdapter = require('./socketio-adapter');
let mainWindow;
let config;
let currentAgent = null;
let currentTerminal = null;
let adapter = null;
let serverStatus = 'disconnected'; // disconnected, connecting, connected, error
let agentConnectionInfo = null;
let healthCheckInterval = null;
// Configuration du systeme de logs
const LOG_DIR = path.join(os.homedir(), '.simpleconnect-ng');
const LOG_FILE = path.join(LOG_DIR, 'socketio.log');
function ensureLogDirectory() {
if (!fs.existsSync(LOG_DIR)) {
fs.mkdirSync(LOG_DIR, { recursive: true });
}
}
function stripAnsi(str) {
return str.replace(/\x1b\[[0-9;]*m/g, '');
}
function fileTimestamp() {
return new Date().toLocaleString('fr-FR', {
timeZone: 'Europe/Paris',
day: '2-digit', month: '2-digit', year: 'numeric',
hour: '2-digit', minute: '2-digit', second: '2-digit'
});
}
function logToFile(message, data = null) {
ensureLogDirectory();
let line = `[${fileTimestamp()}] ${stripAnsi(message)}`;
if (data) line += ' ' + JSON.stringify(data);
fs.appendFileSync(LOG_FILE, line + '\n', 'utf8');
}
function logSectionToFile(message) {
ensureLogDirectory();
fs.appendFileSync(LOG_FILE, `[${fileTimestamp()}] ── ${stripAnsi(message)} ──\n`, 'utf8');
}
// ANSI colors
const c = {
reset: '\x1b[0m',
dim: '\x1b[2m',
green: '\x1b[32m',
yellow: '\x1b[33m',
red: '\x1b[31m',
cyan: '\x1b[36m',
bold: '\x1b[1m',
};
function formatTimestamp() {
return new Date().toLocaleString('fr-FR', {
timeZone: 'Europe/Paris',
hour: '2-digit', minute: '2-digit', second: '2-digit'
});
}
function log(message, data = null) {
console.log(`${c.dim}${formatTimestamp()}${c.reset} ${message}`);
logToFile(message, data);
}
function logBanner(version, serverUrl) {
console.log('');
console.log(` ${c.bold}${c.cyan}SimpleConnect${c.reset} ${c.dim}v${version}${c.reset}`);
console.log(` ${c.dim}Serveur${c.reset} ${serverUrl}`);
console.log('');
logSectionToFile(`SimpleConnect v${version} — ${serverUrl}`);
}
// Charger la configuration
function loadConfig() {
const configPath = path.join(__dirname, 'config.json');
const configData = fs.readFileSync(configPath, 'utf8');
config = JSON.parse(configData);
}
// Creer la fenetre principale
function createWindow() {
mainWindow = new BrowserWindow({
width: 1400,
height: 900,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
webviewTag: true,
webSecurity: false
},
icon: path.join(__dirname, 'icon.png'),
title: `SimpleConnect v${app.getVersion()}`,
autoHideMenuBar: true
});
mainWindow.setMenuBarVisibility(false);
mainWindow.loadFile('index.html');
mainWindow.webContents.on('did-finish-load', () => {
mainWindow.setTitle(`SimpleConnect v${app.getVersion()}`);
});
if (process.env.NODE_ENV === 'development') {
mainWindow.webContents.openDevTools();
}
mainWindow.on('closed', () => {
mainWindow = null;
});
}
// === GESTION SOCKET.IO ===
function initializeSocketIO() {
if (!config.socketio || !config.socketio.serverUrl) {
log('Socket.IO non configuré');
serverStatus = 'disabled';
sendServerStatus();
return;
}
logBanner(app.getVersion(), config.socketio.serverUrl);
adapter = new SocketIOAdapter(config.socketio.serverUrl);
// Demarrer le health check (ecran de login)
startHealthCheck();
}
// Health check periodique via GET /health
// Actif uniquement sur l'ecran de login (pas d'agent connecte)
function startHealthCheck() {
const checkHealth = () => {
// Ne pas faire de health check quand un agent est connecte
// (c'est l'adapter Socket.IO qui pilote le voyant)
if (currentAgent) return;
const serverUrl = config.socketio.serverUrl;
let done = false;
const request = net.request(`${serverUrl}/health`);
request.on('response', (response) => {
if (done) return;
done = true;
const newStatus = response.statusCode === 200 ? 'connected' : 'error';
if (serverStatus !== newStatus) {
serverStatus = newStatus;
sendServerStatus();
}
});
request.on('error', () => {
if (done) return;
done = true;
if (serverStatus !== 'error') {
serverStatus = 'error';
sendServerStatus();
}
});
// Timeout 5s — net.request n'a pas de timeout natif
setTimeout(() => {
if (!done) {
done = true;
request.abort();
if (serverStatus !== 'error') {
serverStatus = 'error';
sendServerStatus();
}
}
}, 5000);
request.end();
};
// Check immediat puis toutes les 5s
checkHealth();
healthCheckInterval = setInterval(checkHealth, 5000);
}
function stopHealthCheck() {
if (healthCheckInterval) {
clearInterval(healthCheckInterval);
healthCheckInterval = null;
}
}
function sendServerStatus() {
if (serverStatus !== 'connected') {
log(`${c.red}✗${c.reset} Serveur injoignable`);
}
if (mainWindow && !mainWindow.isDestroyed()) {
mainWindow.webContents.send('server-status', serverStatus);
}
}
// Gerer un appel entrant
function handleCallPickedUp(event) {
if (!mainWindow || !agentConnectionInfo) return;
const centres = processApplicationUrls(agentConnectionInfo.connList);
const centre = centres.find(c => c.queueName === event.queue_name);
if (centre) {
log(`Basculement vers le centre: ${centre.nom}`);
mainWindow.webContents.send('switch-to-center', {
centreId: centre.id,
centreName: centre.nom,
queue_name: event.queue_name,
terminal: event.terminal,
eventType: 'call_pickup'
});
} else {
log(`${c.yellow}Aucun centre trouvé pour la file: ${event.queue_name}${c.reset}`);
}
}
// Gerer la fin d'un appel
function handleCallHungUp(event) {
if (!mainWindow) return;
log(`Fin d'appel sur la file: ${event.queue_name}`);
mainWindow.webContents.send('release-center', {
queue_name: event.queue_name,
terminal: event.terminal,
eventType: 'call_hangup'
});
}
// Configurer les handlers d'evenements Socket.IO apres connexion
function setupEventHandlers() {
if (!adapter) return;
log('Session Socket.IO démarrée', {
serverUrl: config.socketio.serverUrl,
serviceProvider: config.socketio.serviceProvider
});
// Ecouter les evenements d'appels IPBX (contrat snake_case du serveur)
adapter.on('call_event', (data) => {
log('call_event recu', data);
if (!agentConnectionInfo) return;
// Verifier que l'evenement est pour notre terminal
if (data.terminal !== currentTerminal) {
log(`Événement ignoré — terminal différent: ${data.terminal} !== ${currentTerminal}`);
return;
}
switch (data.event_code) {
case 1:
handleCallPickedUp(data);
break;
case 2:
handleCallHungUp(data);
break;
default:
log('Code evenement non gere:', data);
}
});
}
// Initialisation de l'application
app.whenReady().then(() => {
const { Menu } = require('electron');
Menu.setApplicationMenu(null);
session.defaultSession.webRequest.onBeforeSendHeaders((details, callback) => {
details.requestHeaders['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36';
callback({ requestHeaders: details.requestHeaders });
});
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
callback({
responseHeaders: {
...details.responseHeaders,
'Content-Security-Policy': ['default-src * \'unsafe-inline\' \'unsafe-eval\' data: blob:;']
}
});
});
loadConfig();
createWindow();
initializeSocketIO();
});
// Quitter quand toutes les fenetres sont fermees
app.on('window-all-closed', async () => {
if (healthCheckInterval) {
clearInterval(healthCheckInterval);
}
// Déconnexion propre avant de quitter
if (currentAgent && adapter) {
try {
await adapter.logoff();
} catch (error) {
log(`${c.red}Erreur déconnexion: ${error.message}${c.reset}`);
}
}
if (process.platform !== 'darwin') {
log(`${c.dim}Application fermée${c.reset}`);
logSectionToFile('Application fermée');
app.quit();
}
});
app.on('activate', () => {
if (mainWindow === null) {
createWindow();
}
});
// === IPC HANDLERS ===
ipcMain.handle('get-config', () => {
return config;
});
ipcMain.handle('get-app-version', () => {
return app.getVersion();
});
ipcMain.handle('get-server-status', () => {
return serverStatus;
});
// Recuperer la liste des terminaux via REST
ipcMain.handle('get-terminal-list', async () => {
if (!config.socketio || !config.socketio.serverUrl) {
return [];
}
try {
const provider = config.socketio.serviceProvider;
const url = `${config.socketio.serverUrl}/terminals?provider=${encodeURIComponent(provider)}`;
return new Promise((resolve, reject) => {
const request = net.request(url);
let body = '';
request.on('response', (response) => {
response.on('data', (chunk) => {
body += chunk.toString();
});
response.on('end', () => {
if (response.statusCode === 200) {
try {
const terminals = JSON.parse(body);
log(`${c.green}✓${c.reset} Serveur connecté — ${terminals.length} terminaux`);
resolve(Array.isArray(terminals) ? terminals : []);
} catch (e) {
log(`${c.red}Erreur parsing terminaux: ${e.message}${c.reset}`);
resolve([]);
}
} else {
log(`${c.red}Erreur terminaux HTTP ${response.statusCode}${c.reset}`);
resolve([]);
}
});
});
request.on('error', (error) => {
log(`${c.red}Erreur récupération terminaux: ${error.message}${c.reset}`);
resolve([]);
});
request.end();
});
} catch (error) {
log(`${c.red}Erreur récupération terminaux: ${error.message}${c.reset}`);
return [];
}
});
// Connexion agent via Socket.IO
ipcMain.handle('login-agent', async (event, credentials) => {
if (!adapter) {
return {
success: false,
message: 'Socket.IO non initialise. Veuillez reessayer.'
};
}
try {
log(`Tentative de connexion agent: ${credentials.email} Terminal: ${credentials.terminal}`);
// Deconnecter l'adapter precedent s'il y en a un
if (adapter.state === 'connected') {
adapter.disconnect();
}
// Recreer l'adapter pour une connexion fraiche
adapter = new SocketIOAdapter(config.socketio.serverUrl);
// L'adapter pilote le voyant une fois connecte
adapter.onStateChange((state) => {
log('Etat Socket.IO change', { state });
switch (state) {
case 'reconnecting':
serverStatus = 'connecting';
sendServerStatus();
break;
case 'connected':
serverStatus = 'connected';
sendServerStatus();
break;
case 'error':
case 'disconnected':
serverStatus = 'error';
sendServerStatus();
break;
}
});
// Configurer les handlers d'evenements AVANT connect
setupEventHandlers();
// Connexion avec auth au handshake
const result = await adapter.connect(
credentials.email,
credentials.password,
credentials.terminal
);
if (result) {
log(`${c.green}✓${c.reset} Connexion réussie: ${result.accessCode} (${result.firstName} ${result.lastName})`);
log('Connexion agent réussie', {
accessCode: result.accessCode,
firstName: result.firstName,
lastName: result.lastName,
terminal: credentials.terminal,
connListCount: result.connList ? result.connList.length : 0,
connList: result.connList
});
agentConnectionInfo = result;
currentTerminal = credentials.terminal;
currentAgent = {
id: result.accessCode,
accessCode: result.accessCode,
name: `${result.firstName} ${result.lastName}`,
email: credentials.email,
firstName: result.firstName,
lastName: result.lastName,
terminal: credentials.terminal
};
const centres = processApplicationUrls(result.connList);
if (mainWindow) {
mainWindow.setTitle(
`SimpleConnect v${app.getVersion()} - Agent: ${currentAgent.accessCode} (${result.firstName} ${result.lastName}) - Tel: ${credentials.terminal}`
);
}
// Agent connecte → l'adapter pilote le voyant, on arrete le health check
stopHealthCheck();
serverStatus = 'connected';
sendServerStatus();
return {
success: true,
agent: currentAgent,
centres: centres
};
}
return { success: false, message: 'Echec de l\'authentification' };
} catch (error) {
log(`${c.red}Erreur connexion agent: ${error.message}${c.reset}`);
return {
success: false,
message: error.message || 'Erreur de connexion au serveur'
};
}
});
// Traiter les URLs des applications avec les placeholders
function processApplicationUrls(connList) {
if (!connList || connList.length === 0) return [];
return connList.map((conn, index) => {
let url = conn.applicationName;
// Remplacer les placeholders
if (url.includes('#CA#')) {
url = url.replace('#CA#', conn.accessCode || '');
}
if (url.includes('#MP#')) {
url = url.replace('#MP#', conn.password || '');
}
// Gerer les cas specifiques des plateformes connues
if (url === 'pro.mondocteur.fr' || url.includes('mondocteur.fr')) {
if (!url.startsWith('http')) {
url = 'https://pro.mondocteur.fr/backoffice.do';
}
} else if (url === 'pro.doctolib.fr' || url.includes('doctolib.fr')) {
if (!url.startsWith('http')) {
url = 'https://pro.doctolib.fr/signin';
}
} else if (!url.startsWith('http')) {
url = `https://${url}`;
}
return {
id: conn.code || `centre${index + 1}`,
nom: conn.queueName || conn.code || `Centre ${index + 1}`,
url: url,
couleur: getColorForIndex(index),
credentials: {
username: conn.accessCode,
password: conn.password
},
queueName: conn.queueName
};
});
}
function getColorForIndex(index) {
const colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#DDA0DD', '#FFD93D', '#6BCB77'];
return colors[index % colors.length];
}
// Deconnexion agent via Socket.IO
ipcMain.handle('logout', async () => {
if (currentAgent && adapter) {
try {
log('Logoff agent', {
accessCode: currentAgent.accessCode
});
await adapter.logoff();
log('Agent déconnecté du serveur');
log('Agent déconnecté avec succès', {
accessCode: currentAgent.accessCode,
name: currentAgent.name
});
} catch (error) {
log(`${c.red}Erreur déconnexion: ${error.message}${c.reset}`);
}
}
currentAgent = null;
currentTerminal = null;
agentConnectionInfo = null;
// Retour ecran login → relancer le health check
startHealthCheck();
if (mainWindow) {
mainWindow.setTitle(`SimpleConnect v${app.getVersion()}`);
}
return { success: true };
});
ipcMain.handle('quit-app', async () => {
log(`${c.dim}Application fermée${c.reset}`);
logSectionToFile('Application fermée');
app.quit();
});
ipcMain.handle('get-current-agent', () => {
if (!currentAgent || !agentConnectionInfo) return null;
const centres = processApplicationUrls(agentConnectionInfo.connList);
return {
agent: currentAgent,
centres: centres,
terminal: currentTerminal
};
});
// Sauvegarder les notes de l'agent
ipcMain.handle('save-notes', (event, noteData) => {
const notesDir = path.join(__dirname, 'notes');
if (!fs.existsSync(notesDir)) {
fs.mkdirSync(notesDir);
}
const fileName = `notes_${currentAgent.id}.json`;
const filePath = path.join(notesDir, fileName);
let notesData = {
agent: currentAgent.id,
agentName: currentAgent.name,
currentNote: noteData.content,
lastModified: new Date().toISOString(),
centre: noteData.centre,
history: []
};
if (fs.existsSync(filePath)) {
try {
const existingData = JSON.parse(fs.readFileSync(filePath, 'utf8'));
if (existingData.currentNote && existingData.currentNote !== noteData.content) {
notesData.history = existingData.history || [];
notesData.history.unshift({
content: existingData.currentNote,
date: existingData.lastModified,
centre: existingData.centre
});
notesData.history = notesData.history.slice(0, 50);
}
} catch (error) {
log(`${c.red}Erreur lecture notes existantes: ${error.message}${c.reset}`);
}
}
fs.writeFileSync(filePath, JSON.stringify(notesData, null, 2));
return { success: true, file: fileName };
});
ipcMain.handle('get-notes', () => {
if (!currentAgent) return null;
const notesDir = path.join(__dirname, 'notes');
const fileName = `notes_${currentAgent.id}.json`;
const filePath = path.join(notesDir, fileName);
if (fs.existsSync(filePath)) {
try {
const data = fs.readFileSync(filePath, 'utf8');
return JSON.parse(data);
} catch (error) {
log(`${c.red}Erreur lecture notes: ${error.message}${c.reset}`);
return null;
}
}
return null;
});
ipcMain.handle('get-call-history', () => {
const historyFile = path.join(__dirname, 'call_history.json');
if (fs.existsSync(historyFile)) {
const data = fs.readFileSync(historyFile, 'utf8');
return JSON.parse(data);
}
return [];
});
ipcMain.handle('save-call-history', (event, callData) => {
const historyFile = path.join(__dirname, 'call_history.json');
let history = [];
if (fs.existsSync(historyFile)) {
const data = fs.readFileSync(historyFile, 'utf8');
history = JSON.parse(data);
}
history.unshift({
...callData,
agentId: currentAgent?.id,
timestamp: new Date().toISOString()
});
history = history.slice(0, 100);
fs.writeFileSync(historyFile, JSON.stringify(history, null, 2));
return { success: true };
});
+92
View File
@@ -0,0 +1,92 @@
{
"name": "simpleconnect-electron",
"version": "2.0.2",
"description": "Application de gestion centralisée des plannings médicaux pour centres d'appels",
"main": "main.js",
"scripts": {
"start": "electron .",
"dev": "NODE_ENV=development electron .",
"build": "electron-builder",
"build:win": "electron-builder --win",
"build:mac": "electron-builder --mac",
"build:linux": "electron-builder --linux",
"build:linux-x64": "electron-builder --linux --x64",
"build:linux-arm64": "electron-builder --linux --arm64",
"dist": "electron-builder",
"postinstall": "electron-builder install-app-deps",
"test": "bun test"
},
"keywords": [
"electron",
"medical",
"planning",
"call-center",
"cti"
],
"author": "SimpleConnect",
"license": "MIT",
"devDependencies": {
"electron": "^28.0.0",
"electron-builder": "^24.9.1"
},
"build": {
"appId": "com.simpleconnect.planning",
"productName": "SimpleConnect",
"directories": {
"output": "dist"
},
"files": [
"**/*",
"!dist${/*}",
"!node_modules${/*}/.bin",
"!.git${/*}",
"!README.md"
],
"mac": {
"category": "public.app-category.business",
"icon": "icon.icns"
},
"win": {
"target": "nsis",
"icon": "icon.ico"
},
"linux": {
"target": [
{
"target": "AppImage",
"arch": [
"x64"
]
}
],
"icon": "icon.png",
"category": "Office",
"maintainer": "SimpleConnect Team",
"vendor": "SimpleConnect",
"synopsis": "Gestion centralisée des plannings médicaux",
"description": "Application de gestion centralisée des plannings médicaux pour centres d'appels avec intégration CTI",
"desktop": {
"StartupNotify": "true",
"Encoding": "UTF-8",
"MimeType": "x-scheme-handler/simpleconnect"
}
},
"nsis": {
"oneClick": false,
"perMachine": true,
"allowElevation": true,
"allowToChangeInstallationDirectory": true,
"createDesktopShortcut": true,
"createStartMenuShortcut": true,
"shortcutName": "SimpleConnect"
}
},
"repository": {
"type": "git",
"url": "https://github.com/simpleconnect/electron-app.git"
},
"dependencies": {
"choices.js": "^11.1.0",
"socket.io-client": "^4.8.1"
}
}
+62
View File
@@ -0,0 +1,62 @@
# SimpleConnect v1.3.1
**Date de release : 17 octobre 2025**
## 🎯 Améliorations UX et personnalisation
Cette version apporte des améliorations significatives à l'expérience utilisateur, notamment la possibilité de saisir manuellement un numéro de poste téléphonique.
## ✨ Nouveautés principales
### Saisie manuelle de postes téléphoniques personnalisés
- **Flexibilité accrue** : L'utilisateur peut maintenant taper un numéro de poste qui n'est pas dans la liste officielle
- **Validation intelligente** : Format numérique uniquement avec message d'erreur explicite
- **Avertissement non-bloquant** : Message orange pendant 5 secondes si le poste n'est pas officiel
- **Configuration Choices.js** : Activation de `addItems` et `addChoices` pour les éléments `<select>`
- **Textes UX améliorés** : "Rechercher ou saisir un poste..." pour guider l'utilisateur
### Interface simplifiée
- **Affichage des terminaux épuré** : Les numéros de postes s'affichent sans le préfixe "Poste"
- Avant : "Poste 2101"
- Après : "2101"
- **Liste plus compacte** : Gain d'espace et meilleure lisibilité
## 🐛 Corrections
### Contraste amélioré dans la liste déroulante
- **Fond bleu clair** (#e8f0fe) au lieu de bleu foncé (#667eea)
- **Texte noir foncé** (#1a1a1a) au lieu de blanc
- **Police semi-grasse** (500) pour une meilleure lisibilité
- **Résolution** : L'élément survolé/navigué au clavier est maintenant parfaitement lisible
## 📦 Fichiers disponibles
- `SimpleConnect-1.3.1.AppImage` - Pour Linux (toutes distributions)
- `SimpleConnect-1.3.1-arm64.dmg` - Pour macOS Apple Silicon
- `SimpleConnect-1.3.1-arm64-mac.zip` - Archive macOS alternative
## ✅ Compatibilité
### Configuration serveur
Le serveur accepte maintenant tout numéro de poste valide (format numérique), pas seulement ceux de la liste officielle.
### Plateformes supportées
- ✅ **Linux** x64 (production)
- ✅ **macOS** Apple Silicon (développement)
- ✅ **Windows** (via builds manuels)
## 🔧 Utilisation
### Saisie manuelle d'un poste
1. Cliquer sur le champ "Sélectionner un poste téléphonique"
2. Taper le numéro souhaité (ex: "9999")
3. Appuyer sur Entrée
4. Un avertissement s'affiche si le poste n'est pas dans la liste officielle
5. La connexion est autorisée quand même
### Navigation clavier améliorée
- ↑↓ : Naviguer dans la liste
- Entrée : Valider la sélection
- Les éléments surlignés sont maintenant lisibles
---
*Version améliorant l'expérience utilisateur avec plus de flexibilité et de lisibilité*
+73
View File
@@ -0,0 +1,73 @@
# SimpleConnect v1.4.0 - Affichage de la version
**Date de release** : 21 octobre 2025
## 📋 Résumé
Cette version apporte l'affichage clair et visible de la version de l'application, aussi bien dans l'interface utilisateur que dans la barre de titre native de la fenêtre. Cette amélioration facilite l'identification de la version installée pour le support technique et les utilisateurs.
## ✨ Nouveautés principales
### Affichage de la version dans l'interface
- **Page de connexion** : La version s'affiche désormais sous le titre "SimpleConnect" de manière centrée et élégante (texte gris clair)
- **Page principale** : La version apparaît également dans le header à côté du logo
- **Injection automatique** : La version est récupérée automatiquement depuis `package.json` via un nouveau handler IPC `get-app-version`
### Titre de fenêtre enrichi
- **Barre de titre native** : Affiche maintenant "SimpleConnect v1.4.0" au lieu de simplement "SimpleConnect"
- **Mise à jour dynamique** : Lors de la connexion d'un agent, le titre devient "SimpleConnect v1.4.0 - Agent: XXX (Nom Prénom) - Tel: XXXX"
- **Forçage après chargement** : Résolution du conflit entre le `<title>` HTML et le titre de `BrowserWindow` grâce à un listener `did-finish-load`
## 🔧 Améliorations techniques
- Nouveau style CSS `.app-version-login` pour l'affichage centré sur la page de connexion
- Nouveau style CSS `.app-version` pour l'affichage dans le header principal
- Utilisation de `app.getVersion()` pour récupérer dynamiquement la version depuis `package.json`
- Handler IPC `get-app-version` exposant la version au renderer process
## 📦 Fichiers disponibles
### macOS (Apple Silicon - ARM64)
- `SimpleConnect-1.4.0-arm64.dmg` (96 MB) - Image disque DMG
- `SimpleConnect-1.4.0-arm64-mac.zip` (93 MB) - Archive ZIP portable
### Linux (x64)
- `SimpleConnect-1.4.0.AppImage` (106 MB) - AppImage exécutable
## 🔄 Compatibilité
- **macOS** : 10.12+ (Sierra et versions ultérieures)
- **Linux** : Distributions x64 supportant AppImage
- **Electron** : 28.3.3
- **Node.js** : Intégré dans Electron
## 📝 Installation
### macOS
1. Télécharger `SimpleConnect-1.4.0-arm64.dmg`
2. Ouvrir le fichier DMG
3. Glisser SimpleConnect dans le dossier Applications
4. Lancer depuis Applications
### Linux
1. Télécharger `SimpleConnect-1.4.0.AppImage`
2. Rendre le fichier exécutable : `chmod +x SimpleConnect-1.4.0.AppImage`
3. Double-cliquer ou exécuter : `./SimpleConnect-1.4.0.AppImage`
## 🔗 Changelog complet
Pour voir l'historique complet des modifications, consultez le fichier [CHANGELOG.md](../docs/changelog.md).
## 🐛 Problèmes connus
Aucun problème connu pour cette version.
## 💡 Support
Pour toute question ou problème, contactez l'équipe SimpleConnect.
---
**Version précédente** : [v1.3.1](v1.3.1.md)
+68
View File
@@ -0,0 +1,68 @@
# SimpleConnect v1.4.1 - Bouton Quitter sur la page de login
**Date de release** : 21 octobre 2025
## 📋 Résumé
Cette version mineure apporte un bouton "Quitter" sur la page de connexion, permettant aux utilisateurs de fermer proprement l'application sans avoir à se connecter. Cette amélioration répond à un besoin d'ergonomie pour les utilisateurs qui lancent l'application par erreur ou qui changent d'avis avant la connexion.
## ✨ Nouveautés principales
### Bouton "Quitter" sur la page de connexion
- **Positionnement** : Le bouton est placé sous le bouton "Se connecter" sur la page de login
- **Style visuel** : Bouton gris secondaire pour le différencier du bouton principal bleu
- **Comportement** : Clic sur le bouton ferme immédiatement et proprement l'application
- **Fonction** : Utilise le handler IPC `quit-app` existant pour une fermeture propre
## 🔧 Améliorations techniques
- Nouveau style CSS `.btn-quit` avec couleur grise (`#6c757d`) et effet hover (`#5a6268`)
- Fonction `handleQuitFromLogin()` dédiée à la fermeture depuis la page de login
- Réutilisation du handler IPC `quit-app` déjà présent dans l'application
- Marge supérieure de 10px pour un espacement visuel optimal
## 📦 Fichiers disponibles
### macOS (Apple Silicon - ARM64)
- `SimpleConnect-1.4.1-arm64.dmg` (96 MB) - Image disque DMG
- `SimpleConnect-1.4.1-arm64-mac.zip` (93 MB) - Archive ZIP portable
### Linux (x64)
- `SimpleConnect-1.4.1.AppImage` (106 MB) - AppImage exécutable
## 🔄 Compatibilité
- **macOS** : 10.12+ (Sierra et versions ultérieures)
- **Linux** : Distributions x64 supportant AppImage
- **Electron** : 28.3.3
- **Node.js** : Intégré dans Electron
## 📝 Installation
### macOS
1. Télécharger `SimpleConnect-1.4.1-arm64.dmg`
2. Ouvrir le fichier DMG
3. Glisser SimpleConnect dans le dossier Applications
4. Lancer depuis Applications
### Linux
1. Télécharger `SimpleConnect-1.4.1.AppImage`
2. Rendre le fichier exécutable : `chmod +x SimpleConnect-1.4.1.AppImage`
3. Double-cliquer ou exécuter : `./SimpleConnect-1.4.1.AppImage`
## 🔗 Changelog complet
Pour voir l'historique complet des modifications, consultez le fichier [CHANGELOG.md](../docs/changelog.md).
## 🐛 Problèmes connus
Aucun problème connu pour cette version.
## 💡 Support
Pour toute question ou problème, contactez l'équipe SimpleConnect.
---
**Version précédente** : [v1.4.0](v1.4.0.md)
+1281
View File
File diff suppressed because it is too large Load Diff
+186
View File
@@ -0,0 +1,186 @@
/**
* Adaptateur Socket.IO natif pour le serveur Python SimpleServer
*
* Connexion directe au port 8004, auth au handshake,
* reconnexion native illimitee.
*/
const io = require('socket.io-client');
class SocketIOAdapter {
constructor(serverUrl, socketFactory = null) {
this.serverUrl = serverUrl;
this._socketFactory = socketFactory || io;
this.socket = null;
this._state = 'disconnected'; // disconnected, connecting, connected, reconnecting, error
this._eventHandlers = new Map();
this._onStateChange = null; // callback pour notifier main.js des changements d'etat
}
/**
* Enregistrer un callback de changement d'etat.
* @param {function(string)} callback - recoit le nouvel etat
*/
onStateChange(callback) {
this._onStateChange = callback;
}
_setState(state) {
this._state = state;
if (this._onStateChange) {
this._onStateChange(state);
}
}
/**
* Connexion avec auth au handshake.
* Le serveur authentifie dans le handler connect et emet 'login_ok' avec le resultat.
* @returns {Promise<object>} connResult (accessCode, firstName, lastName, connList)
*/
connect(accessCode, password, terminal) {
return new Promise((resolve, reject) => {
this._setState('connecting');
this.socket = this._socketFactory(this.serverUrl, {
auth: { access_code: accessCode, password, terminal },
transports: ['websocket'],
reconnection: true,
reconnectionAttempts: Infinity,
reconnectionDelay: 2000,
reconnectionDelayMax: 10000,
});
let settled = false;
// Le serveur emet 'login_ok' avec les donnees de session
this.socket.once('login_ok', (data) => {
if (settled) return;
settled = true;
this._setState('connected');
resolve(data);
});
// Le serveur emet 'login_error' si auth echouee (avant return false)
this.socket.once('login_error', (data) => {
if (settled) return;
settled = true;
this._setState('error');
this.socket.disconnect();
reject(new Error(data.message || 'Authentification refusee'));
});
// Erreur de connexion (serveur injoignable ou return false du handler connect)
this.socket.on('connect_error', (err) => {
if (!settled) {
settled = true;
this._setState('error');
this.socket.disconnect();
reject(new Error(err.message || 'Connexion refusee'));
}
// Apres login reussi : reconnexion auto, on passe en 'reconnecting'
});
// Timeout de connexion initiale (15s)
setTimeout(() => {
if (!settled) {
settled = true;
this._setState('error');
this.socket.disconnect();
reject(new Error('Timeout de connexion au serveur'));
}
}, 15000);
// === Handlers de reconnexion (actifs apres le premier login) ===
// Deconnexion temporaire → passer en 'reconnecting'
this.socket.on('disconnect', (reason) => {
if (this._state === 'connected') {
// Deconnexion temporaire, socket.io va retenter
this._setState('reconnecting');
}
});
// Reconnexion reussie → le serveur recoit un nouveau connect avec les memes auth
// et emet 'login_ok' (reprise de session)
this.socket.on('login_ok', (data) => {
if (settled && this._state === 'reconnecting') {
this._setState('connected');
}
});
// Restaurer les handlers enregistres avant connect
this._eventHandlers.forEach((handler, event) => {
this.socket.on(event, handler);
});
});
}
/**
* Deconnexion volontaire avec logoff IPBX.
* Emet 'logout' et attend 'logout_ok' du serveur.
*/
logoff() {
return new Promise((resolve) => {
if (!this.socket || !this.socket.connected) {
this._setState('disconnected');
resolve();
return;
}
const timeout = setTimeout(() => {
if (this.socket) {
this.socket.disconnect();
}
this._setState('disconnected');
resolve();
}, 5000);
this.socket.once('logout_ok', () => {
clearTimeout(timeout);
this._setState('disconnected');
resolve();
});
this.socket.emit('logout');
});
}
/**
* Deconnexion brute (sans logoff IPBX).
*/
disconnect() {
if (this.socket) {
this.socket.disconnect();
}
this._setState('disconnected');
}
/**
* Ecouter un evenement serveur.
*/
on(event, handler) {
this._eventHandlers.set(event, handler);
if (this.socket) {
this.socket.on(event, handler);
}
}
/**
* Retirer un handler d'evenement.
*/
off(event) {
this._eventHandlers.delete(event);
if (this.socket) {
this.socket.off(event);
}
}
/**
* Etat de la connexion.
*/
get state() {
return this._state;
}
}
module.exports = SocketIOAdapter;
+190
View File
@@ -0,0 +1,190 @@
import { describe, test, expect, mock, beforeEach } from "bun:test";
const SocketIOAdapter = require("./socketio-adapter.js");
// EventEmitter minimal pour simuler un socket.io
function createFakeSocket() {
const listeners = {};
const socket = {
on(event, fn) { (listeners[event] ??= []).push(fn); },
once(event, fn) {
const wrapper = (...args) => {
socket.off(event, wrapper);
fn(...args);
};
(listeners[event] ??= []).push(wrapper);
},
off(event, fn) {
if (fn) {
listeners[event] = (listeners[event] || []).filter(f => f !== fn);
} else {
delete listeners[event];
}
},
emit: mock(() => {}),
disconnect: mock(() => {}),
connected: true,
// Helper test : déclencher un event côté "serveur"
_fire(event, ...args) {
(listeners[event] || []).slice().forEach(fn => fn(...args));
},
};
return socket;
}
describe("SocketIOAdapter", () => {
let adapter, socket;
beforeEach(() => {
socket = null;
const factory = (url, opts) => {
socket = createFakeSocket();
return socket;
};
adapter = new SocketIOAdapter("http://localhost:8004", factory);
});
test("état initial = disconnected", () => {
expect(adapter.state).toBe("disconnected");
});
test("connect() passe en connecting puis connected sur login_ok", async () => {
const states = [];
adapter.onStateChange((s) => states.push(s));
const p = adapter.connect("1234", "pass", "2001");
socket._fire("login_ok", { accessCode: "1234", firstName: "Test", lastName: "User", connList: [] });
const result = await p;
expect(states).toEqual(["connecting", "connected"]);
expect(adapter.state).toBe("connected");
expect(result.accessCode).toBe("1234");
});
test("connect() rejette sur login_error", async () => {
const p = adapter.connect("1234", "wrong", "2001");
socket._fire("login_error", { message: "Mot de passe incorrect" });
expect(p).rejects.toThrow("Mot de passe incorrect");
expect(adapter.state).toBe("error");
});
test("connect() rejette sur connect_error", async () => {
const p = adapter.connect("1234", "pass", "2001");
socket._fire("connect_error", new Error("Connection refused"));
expect(p).rejects.toThrow("Connection refused");
expect(adapter.state).toBe("error");
});
test("déconnexion temporaire passe en reconnecting", async () => {
const p = adapter.connect("1234", "pass", "2001");
socket._fire("login_ok", { accessCode: "1234" });
await p;
socket._fire("disconnect", "transport close");
expect(adapter.state).toBe("reconnecting");
});
test("reconnexion après déconnexion repasse en connected", async () => {
const p = adapter.connect("1234", "pass", "2001");
socket._fire("login_ok", { accessCode: "1234" });
await p;
socket._fire("disconnect", "transport close");
expect(adapter.state).toBe("reconnecting");
socket._fire("login_ok", { accessCode: "1234" });
expect(adapter.state).toBe("connected");
});
test("logoff() émet logout et passe en disconnected sur logout_ok", async () => {
const p = adapter.connect("1234", "pass", "2001");
socket._fire("login_ok", { accessCode: "1234" });
await p;
const logoffP = adapter.logoff();
socket._fire("logout_ok");
await logoffP;
expect(socket.emit).toHaveBeenCalledWith("logout");
expect(adapter.state).toBe("disconnected");
});
test("onStateChange est appelé à chaque transition", async () => {
const states = [];
adapter.onStateChange((s) => states.push(s));
const p = adapter.connect("1234", "pass", "2001");
socket._fire("login_ok", { accessCode: "1234" });
await p;
socket._fire("disconnect", "transport close");
socket._fire("login_ok", { accessCode: "1234" });
expect(states).toEqual(["connecting", "connected", "reconnecting", "connected"]);
});
test("on('call_event') recoit les champs snake_case du serveur", async () => {
const received = [];
adapter.on('call_event', (data) => received.push(data));
const p = adapter.connect("1234", "pass", "2001");
socket._fire("login_ok", { accessCode: "1234" });
await p;
// Le serveur envoie un call_event avec champs snake_case
socket._fire("call_event", {
event_code: 1,
queue_name: "FILE_CENTRE_A",
terminal: "2001",
});
expect(received).toHaveLength(1);
expect(received[0].event_code).toBe(1);
expect(received[0].queue_name).toBe("FILE_CENTRE_A");
expect(received[0].terminal).toBe("2001");
});
test("on('call_event') enregistré avant connect() est dispatché après connexion", async () => {
const received = [];
// Enregistrer le handler AVANT connect (comme le fait main.js)
adapter.on('call_event', (data) => received.push(data));
const p = adapter.connect("1234", "pass", "2001");
socket._fire("login_ok", { accessCode: "1234" });
await p;
// Le serveur envoie un call_event
socket._fire("call_event", {
event_code: 2,
queue_name: "FILE_CENTRE_B",
terminal: "2001",
});
expect(received).toHaveLength(1);
expect(received[0].event_code).toBe(2);
expect(received[0].queue_name).toBe("FILE_CENTRE_B");
});
test("off('call_event') supprime le handler", async () => {
const received = [];
adapter.on('call_event', (data) => received.push(data));
const p = adapter.connect("1234", "pass", "2001");
socket._fire("login_ok", { accessCode: "1234" });
await p;
// Supprimer le handler
adapter.off('call_event');
// L'event ne devrait plus être capté
socket._fire("call_event", { event_code: 1, queue_name: "FILE", terminal: "2001" });
expect(received).toHaveLength(0);
});
});
+1145
View File
File diff suppressed because it is too large Load Diff