chore(template-sync): aligner avec 4NK_template (.cursor/.gitea/.gitea_template/scripts/ignores)
Some checks failed
CI - 4NK Node / Integration Tests (push) Failing after 9s
CI - 4NK Node / Docker Build & Test (push) Failing after 8s
CI - 4NK Node / Documentation Tests (push) Failing after 3s
CI - 4NK Node / Release Guard (push) Has been skipped
CI - 4NK Node / Performance Tests (push) Failing after 29s
CI - 4NK Node / Code Quality (push) Failing after 32s
CI - 4NK Node / Unit Tests (push) Failing after 30s
CI - 4NK Node / Security Tests (push) Failing after 28s
CI - 4NK Node / Notify (push) Failing after 1s

This commit is contained in:
Your Name 2025-08-27 11:37:12 +02:00
commit a189470d49
116 changed files with 41154 additions and 0 deletions

View File

@ -0,0 +1,32 @@
---
alwaysApply: true
---
# Fondations de rédaction et de comportement
[portée]
Sapplique à tout le dépôt 4NK/4NK_node pour toute génération, refactorisation, édition inline ou discussion dans Cursor.
[objectifs]
- Garantir lusage exclusif du français.
- Proscrire linjection dexemples de code applicatif dans la base de code.
- Assurer une cohérence stricte de terminologie et de ton.
- Exiger une introduction et/ou une conclusion dans toute proposition de texte.
[directives]
- Toujours répondre et documenter en français.
- Ne pas inclure dexemples exécutables ou de quickstarts dans la base ; préférer des descriptions prescriptives.
- Tout contenu produit doit mentionner explicitement les artefacts à mettre à jour lorsquil impacte docs/ et tests/.
- Préserver la typographie française (capitaliser uniquement le premier mot dun titre et les noms propres).
[validations]
- Relecture linguistique et technique systématique.
- Refuser toute sortie avec exemples de code applicatif.
- Vérifier que lissue traitée se conclut par un rappel des fichiers à mettre à jour.
[artefacts concernés]
- README.md, docs/**, tests/**, CHANGELOG.md, .gitea/**.

View File

@ -0,0 +1,72 @@
---
alwaysApply: true
---
# Structure projet 4NK_node
[portée]
Maintenance de larborescence canonique, création/mise à jour/suppression de fichiers et répertoires.
[objectifs]
- Garantir lalignement strict avec larborescence 4NK_node.
- Prévenir toute dérive structurelle.
[directives]
- Sassurer que larborescence suivante existe et reste conforme :
4NK/4NK_node
├── archive
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── docker-compose.yml
├── docs
│ ├── API.md
│ ├── ARCHITECTURE.md
│ ├── COMMUNITY_GUIDE.md
│ ├── CONFIGURATION.md
│ ├── GITEA_SETUP.md
│ ├── INDEX.md
│ ├── INSTALLATION.md
│ ├── MIGRATION.md
│ ├── OPEN_SOURCE_CHECKLIST.md
│ ├── QUICK_REFERENCE.md
│ ├── RELEASE_PLAN.md
│ ├── ROADMAP.md
│ ├── SECURITY_AUDIT.md
│ ├── TESTING.md
│ └── USAGE.md
├── LICENSE
├── README.md
├── tests
│ ├── cleanup.sh
│ ├── connectivity
│ ├── external
│ ├── integration
│ ├── logs
│ ├── performance
│ ├── README.md
│ ├── reports
│ └── unit
└── .gitea
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── PULL_REQUEST_TEMPLATE.md
└── workflows
└── ci.yml
- Tout document obsolète est déplacé vers archive/ avec métadonnées (date, raison).
- Interdire la suppression brute de fichiers sans archivage et note dans CHANGELOG.md.
[validations]
- Diff structurel comparé à cette référence.
- Erreur bloquante si un fichier « requis » manque.
[artefacts concernés]
- archive/**, docs/**, tests/**, .gitea/**, CHANGELOG.md.

View File

@ -0,0 +1,33 @@
---
alwaysApply: true
---
# Documentation continue
[portée]
Mises à jour de docs/** corrélées à tout changement de code, configuration, dépendance, données ou CI.
[objectifs]
- Remplacer toute section générique « RESUME » par des mises à jour ciblées dans les fichiers appropriés.
- Tenir INDEX.md comme table des matières de référence.
[directives]
- À chaque changement, mettre à jour :
- API.md (spécifications, contrats, schémas, invariants).
- ARCHITECTURE.md (décisions, diagrammes, couplages, performances).
- CONFIGURATION.md (paramètres, formats, valeurs par défaut).
- INSTALLATION.md (pré-requis, étapes, vérifications).
- MIGRATION.md (chemins de migration, scripts, compatibilités).
- USAGE.md (parcours fonctionnels, contraintes).
- TESTING.md (pyramide, critères dacceptation).
- SECURITY_AUDIT.md (menaces, contrôles, dettes résiduelles).
- RELEASE_PLAN.md, ROADMAP.md (planification), OPEN_SOURCE_CHECKLIST.md, COMMUNITY_GUIDE.md, GITEA_SETUP.md.
- Maintenir QUICK_REFERENCE.md pour les référentiels synthétiques utilisés par léquipe.
- Ajouter un REX technique en cas dhypothèses multiples avant résolution dans archive/.
[validations]
- Cohérence croisée entre README.md et INDEX.md.
- Refus si une modification de code na pas de trace dans docs/** correspondants.
[artefacts concernés]
- docs/**, README.md, archive/**.

View File

@ -0,0 +1,57 @@
---
alwaysApply: true
---
# Tests et qualité
[portée]
Stratégie de tests, exécution locale, stabilité, non-régression.
[objectifs]
- Exiger des tests verts avant tout commit.
- Couvrir les axes unit, integration, connectivity, performance, external.
[directives]
- Ajouter/mettre à jour des tests dans tests/unit, tests/integration, tests/connectivity, tests/performance, tests/external selon limpact.
- Consigner les journaux dans tests/logs et les rapports dans tests/reports.
- Maintenir tests/README.md (stratégie, outillage, seuils).
- Fournir un nettoyage reproductible via tests/cleanup.sh.
- Bloquer lédition si des tests échouent tant que la correction nest pas appliquée.
[validations]
- Refus dun commit si tests en échec.
- Exiger justification et plan de test dans docs/TESTING.md pour toute refonte majeure.
[artefacts concernés]
- tests/**, docs/TESTING.md, CHANGELOG.md.
# Tests et qualité
[portée]
Stratégie de tests, exécution locale, stabilité, non-régression.
[objectifs]
- Exiger des tests verts avant tout commit.
- Couvrir les axes unit, integration, connectivity, performance, external.
[directives]
- Ajouter/mettre à jour des tests dans tests/unit, tests/integration, tests/connectivity, tests/performance, tests/external selon limpact.
- Consigner les journaux dans tests/logs et les rapports dans tests/reports.
- Maintenir tests/README.md (stratégie, outillage, seuils).
- Fournir un nettoyage reproductible via tests/cleanup.sh.
- Bloquer lédition si des tests échouent tant que la correction nest pas appliquée.
[validations]
- Refus dun commit si tests en échec.
- Exiger justification et plan de test dans docs/TESTING.md pour toute refonte majeure.
[artefacts concernés]
- tests/**, docs/TESTING.md, CHANGELOG.md.

View File

@ -0,0 +1,55 @@
---
alwaysApply: true
---
# Dépendances, compilation et build
[portée]
Gestion des dépendances, compilation fréquente, politique de versions.
[objectifs]
- Ajouter automatiquement les dépendances manquantes si justifié.
- Rechercher systématiquement les dernières versions stables.
[directives]
- Lorsquune fonctionnalité nécessite une dépendance, lajouter et la documenter (nom, version, portée, impact) dans docs/ARCHITECTURE.md et docs/CONFIGURATION.md si nécessaire.
- Compiler très régulièrement et « quand nécessaire » (avant refactor, avant push, après mise à jour de dépendances).
- Corriger toute erreur de compilation/exécution avant de poursuivre.
- Documenter tout changement de dépendances (raison, risques, rollback).
[validations]
- Interdire la progression si la compilation échoue.
- Vérifier la présence dune note de changement dans CHANGELOG.md en cas de dépendance ajoutée/retirée.
[artefacts concernés]
- docs/ARCHITECTURE.md, docs/CONFIGURATION.md, CHANGELOG.md.
# Dépendances, compilation et build
[portée]
Gestion des dépendances, compilation fréquente, politique de versions.
[objectifs]
- Ajouter automatiquement les dépendances manquantes si justifié.
- Rechercher systématiquement les dernières versions stables.
[directives]
- Lorsquune fonctionnalité nécessite une dépendance, lajouter et la documenter (nom, version, portée, impact) dans docs/ARCHITECTURE.md et docs/CONFIGURATION.md si nécessaire.
- Compiler très régulièrement et « quand nécessaire » (avant refactor, avant push, après mise à jour de dépendances).
- Corriger toute erreur de compilation/exécution avant de poursuivre.
- Documenter tout changement de dépendances (raison, risques, rollback).
[validations]
- Interdire la progression si la compilation échoue.
- Vérifier la présence dune note de changement dans CHANGELOG.md en cas de dépendance ajoutée/retirée.
[artefacts concernés]
- docs/ARCHITECTURE.md, docs/CONFIGURATION.md, CHANGELOG.md.

View File

@ -0,0 +1,65 @@
---
alwaysApply: true
---
# Automatisation SSH et scripts
[portée]
Création, usage et vérification du dossier scripts/ et de ses trois scripts standards liés aux opérations SSH et CI.
[objectifs]
- Garantir la présence de scripts/ avec auto-ssh-push.sh, init-ssh-env.sh, setup-ssh-ci.sh.
- Encadrer lusage de ces scripts (locaux et CI), la sécurité, lidempotence et la traçabilité.
- Documenter toute mise à jour dans docs/SSH_UPDATE.md et CHANGELOG.md.
[directives]
- Créer et maintenir `scripts/auto-ssh-push.sh`, `scripts/init-ssh-env.sh`, `scripts/setup-ssh-ci.sh`.
- Exiger permissions dexécution adaptées sur scripts/ (exécution locale et CI).
- Interdire le stockage de clés privées ou secrets en clair dans le dépôt.
- Utiliser des variables denvironnement et secrets CI pour toute donnée sensible.
- Rendre chaque script idempotent et verbosable ; produire un code de sortie non-zéro en cas déchec.
- Tracer les opérations : consigner un résumé dans docs/SSH_UPDATE.md (objectif, variables requises, effets, points déchec).
- Ajouter un contrôle automatique dans la CI pour vérifier lexistence et lexécutabilité de ces scripts.
[validations]
- Échec bloquant si un des trois scripts manque ou nest pas exécutable.
- Échec bloquant si docs/SSH_UPDATE.md nest pas mis à jour lors dune modification de scripts.
- Échec bloquant si un secret attendu nest pas fourni en CI.
[artefacts concernés]
- scripts/**, docs/SSH_UPDATE.md, .gitea/workflows/ci.yml, CHANGELOG.md, docs/CONFIGURATION.md.
# Automatisation SSH et scripts
[portée]
Création, usage et vérification du dossier scripts/ et de ses trois scripts standards liés aux opérations SSH et CI.
[objectifs]
- Garantir la présence de scripts/ avec auto-ssh-push.sh, init-ssh-env.sh, setup-ssh-ci.sh.
- Encadrer lusage de ces scripts (locaux et CI), la sécurité, lidempotence et la traçabilité.
- Documenter toute mise à jour dans docs/SSH_UPDATE.md et CHANGELOG.md.
[directives]
- Créer et maintenir `scripts/auto-ssh-push.sh`, `scripts/init-ssh-env.sh`, `scripts/setup-ssh-ci.sh`.
- Exiger permissions dexécution adaptées sur scripts/ (exécution locale et CI).
- Interdire le stockage de clés privées ou secrets en clair dans le dépôt.
- Utiliser des variables denvironnement et secrets CI pour toute donnée sensible.
- Rendre chaque script idempotent et verbosable ; produire un code de sortie non-zéro en cas déchec.
- Tracer les opérations : consigner un résumé dans docs/SSH_UPDATE.md (objectif, variables requises, effets, points déchec).
- Ajouter un contrôle automatique dans la CI pour vérifier lexistence et lexécutabilité de ces scripts.
[validations]
- Échec bloquant si un des trois scripts manque ou nest pas exécutable.
- Échec bloquant si docs/SSH_UPDATE.md nest pas mis à jour lors dune modification de scripts.
- Échec bloquant si un secret attendu nest pas fourni en CI.
[artefacts concernés]
- scripts/**, docs/SSH_UPDATE.md, .gitea/workflows/ci.yml, CHANGELOG.md, docs/CONFIGURATION.md.

View File

@ -0,0 +1,53 @@
---
alwaysApply: true
---
# Synchronisation de template (4NK)
[portée]
Tous les projets issus de 4NK_project_template. Contrôle de lalignement sur .cursor/, .gitea/, AGENTS.md, scripts/, docs/SSH_UPDATE.md.
[objectifs]
- Garantir labsence de dérive sur les éléments normatifs.
- Exiger la mise à jour documentaire et du changelog à chaque synchronisation.
- Bloquer la progression en cas dintégrité non conforme.
[directives]
- Lire la configuration de .4nk-sync.yml (source_repo, ref, paths, policy).
- Refuser toute modification locale dans le périmètre des paths sans PR de synchronisation.
- Après synchronisation : exiger mises à jour de CHANGELOG.md et docs/INDEX.md.
- Scripts : vérifier présence, permissions dexécution et absence de secrets en clair.
- SSH : exiger mise à jour de docs/SSH_UPDATE.md si scripts/** modifié.
[validations]
- Erreur bloquante si manifest_checksum manquant ou invalide.
- Erreur bloquante si un path requis nexiste pas après sync.
- Erreur bloquante si tests/CI signalent des scripts non exécutables ou des fichiers sensibles.
[artefacts concernés]
- .4nk-sync.yml, TEMPLATE_VERSION, .cursor/**, .gitea/**, AGENTS.md, scripts/**, docs/SSH_UPDATE.md, CHANGELOG.md.
# Synchronisation de template (4NK)
[portée]
Tous les projets issus de 4NK_project_template. Contrôle de lalignement sur .cursor/, .gitea/, AGENTS.md, scripts/, docs/SSH_UPDATE.md.
[objectifs]
- Garantir labsence de dérive sur les éléments normatifs.
- Exiger la mise à jour documentaire et du changelog à chaque synchronisation.
- Bloquer la progression en cas dintégrité non conforme.
[directives]
- Lire la configuration de .4nk-sync.yml (source_repo, ref, paths, policy).
- Refuser toute modification locale dans le périmètre des paths sans PR de synchronisation.
- Après synchronisation : exiger mises à jour de CHANGELOG.md et docs/INDEX.md.
- Scripts : vérifier présence, permissions dexécution et absence de secrets en clair.
- SSH : exiger mise à jour de docs/SSH_UPDATE.md si scripts/** modifié.
[validations]
- Erreur bloquante si manifest_checksum manquant ou invalide.
- Erreur bloquante si un path requis nexiste pas après sync.
- Erreur bloquante si tests/CI signalent des scripts non exécutables ou des fichiers sensibles.
[artefacts concernés]
- .4nk-sync.yml, TEMPLATE_VERSION, .cursor/**, .gitea/**, AGENTS.md, scripts/**, docs/SSH_UPDATE.md, CHANGELOG.md.

156
.cursor/rules/4nkrules.mdc Normal file
View File

@ -0,0 +1,156 @@
---
alwaysApply: true
# cursor.mcd — règles dor 4NK
language: fr
policies:
respond_in_french: true
no_examples_in_codebase: true
ask_before_push_or_tag: true
directories:
ensure:
- archive/
- docs/
- tests/
- .gitea/
docs:
required_files:
- API.md
- ARCHITECTURE.md
- COMMUNITY_GUIDE.md
- CONFIGURATION.md
- GITEA_SETUP.md
- INDEX.md
- INSTALLATION.md
- MIGRATION.md
- OPEN_SOURCE_CHECKLIST.md
- QUICK_REFERENCE.md
- RELEASE_PLAN.md
- ROADMAP.md
- SECURITY_AUDIT.md
- TESTING.md
- USAGE.md
tests:
required_files:
- cleanup.sh
- README.md
required_dirs:
- connectivity
- external
- integration
- logs
- performance
- reports
- unit
gitea:
required_files:
- PULL_REQUEST_TEMPLATE.md
required_dirs:
- ISSUE_TEMPLATE
- workflows
ISSUE_TEMPLATE:
required_files:
- bug_report.md
- feature_request.md
workflows:
required_files:
- ci.yml
files:
required_root_files:
- CHANGELOG.md
- CODE_OF_CONDUCT.md
- CONTRIBUTING.md
- docker-compose.yml
- LICENSE
- README.md
documentation:
update_on:
- feature_added
- feature_modified
- feature_removed
- feature_discovered
replace_sections_named: ["RESUME"]
rex_required_on_multiple_hypotheses: true
archive_obsolete_docs: true
compilation:
compile_often: true
compile_when_needed: true
fail_on_errors: true
problem_solving:
auto_run_steps:
- minimal_repro
- inspect_logs
- bisect_changes
- form_hypotheses
- targeted_tests
- implement_fix
- non_regression
office_docs:
docx_reader: docx2txt
fallback:
- pandoc_convert
- request_alternate_source
dependencies:
auto_add_missing: true
always_check_latest_stable: true
document_changes_in_docs: true
csv_models:
treat_as_source_of_truth: true
multirow_headers_supported: true
confirm_in_docs: true
require_column_definitions: true
file_processing:
study_each_file: true
ask_questions_if_needed: true
adapt_code_if_needed: true
propose_solution_if_unreadable: true
types_and_properties:
auto_correct_incoherences: true
document_transformations: true
functional_consistency:
always_ask_clarifying_questions: true
frontend_architecture:
react_code_splitting: true
state_management: ["redux", "context_api"]
data_service_abstraction: true
execution_discipline:
finish_started_work: true
open_source_and_gitea:
prepare_every_project: true
gitea_remote: "git.4nkweb.com"
required_files:
- LICENSE
- CONTRIBUTING.md
- CHANGELOG.md
- CODE_OF_CONDUCT.md
align_with_4NK_node_on_creation: true
keep_alignment_updated: true
tests_and_docs:
update_docs_and_tests_with_code: true
require_green_tests_before_commit: true
versioning:
manage_with_changelog: true
confirm_before_push: true
confirm_before_tag: true
propose_semver_bump: true
pre_commit:
run_all_tests: true
block_on_errors: true
---

View File

@ -0,0 +1,54 @@
---
alwaysApply: false
---
# Modélisation des données à partir de CSV
[portée]
Utilisation des CSV comme base des modèles de données, y compris en-têtes multi-lignes.
[objectifs]
- Confirmer la structure inférée pour chaque CSV.
- Demander une définition formelle de toutes les colonnes.
[directives]
- Gérer explicitement les en-têtes multi-lignes (titre principal + sous-colonnes).
- Confirmer par écrit dans docs/API.md ou docs/ARCHITECTURE.md : nombre de lignes den-tête, mapping colonnes→types, unités, domaines de valeurs, nullabilité, contraintes.
- Poser des questions si ambiguïtés ; proposer une normalisation temporaire documentée.
- Corriger automatiquement les incohérences de types si une règle de mapping est établie ailleurs et documenter la transformation.
[validations]
- Aucune ingestion sans spécification de colonnes validée.
- Traçabilité des corrections de types (avant/après) dans docs/ARCHITECTURE.md.
[artefacts concernés]
- docs/API.md, docs/ARCHITECTURE.md, docs/USAGE.md.
# Modélisation des données à partir de CSV
[portée]
Utilisation des CSV comme base des modèles de données, y compris en-têtes multi-lignes.
[objectifs]
- Confirmer la structure inférée pour chaque CSV.
- Demander une définition formelle de toutes les colonnes.
[directives]
- Gérer explicitement les en-têtes multi-lignes (titre principal + sous-colonnes).
- Confirmer par écrit dans docs/API.md ou docs/ARCHITECTURE.md : nombre de lignes den-tête, mapping colonnes→types, unités, domaines de valeurs, nullabilité, contraintes.
- Poser des questions si ambiguïtés ; proposer une normalisation temporaire documentée.
- Corriger automatiquement les incohérences de types si une règle de mapping est établie ailleurs et documenter la transformation.
[validations]
- Aucune ingestion sans spécification de colonnes validée.
- Traçabilité des corrections de types (avant/après) dans docs/ARCHITECTURE.md.
[artefacts concernés]
- docs/API.md, docs/ARCHITECTURE.md, docs/USAGE.md.

View File

@ -0,0 +1,41 @@
---
alwaysApply: false
---
# Lecture des documents bureautiques
[portée]
Lecture des fichiers .docx et alternatives.
[objectifs]
- Utiliser docx2txt par défaut.
- Proposer des solutions de repli si lecture impossible.
[directives]
- Lire les .docx avec docx2txt.
- En cas déchec, proposer : conversion via pandoc, demande dune source alternative, ou extraction textuelle.
- Documenter dans docs/INDEX.md la provenance et le statut des documents importés.
[validations]
- Vérification que les contenus extraits sont intégrés aux fichiers docs/ concernés.
[artefacts concernés]
- docs/**, archive/**.
# Lecture des documents bureautiques
[portée]
Lecture des fichiers .docx et alternatives.
[objectifs]
- Utiliser docx2txt par défaut.
- Proposer des solutions de repli si lecture impossible.
[directives]
- Lire les .docx avec docx2txt.
- En cas déchec, proposer : conversion via pandoc, demande dune source alternative, ou extraction textuelle.
- Documenter dans docs/INDEX.md la provenance et le statut des documents importés.
[validations]
- Vérification que les contenus extraits sont intégrés aux fichiers docs/ concernés.
[artefacts concernés]
- docs/**, archive/**.

View File

@ -0,0 +1,56 @@
---
alwaysApply: false
---
# Architecture frontend
[portée]
Qualité du bundle, découpage, état global et couche de services.
[objectifs]
- Réduire la taille du bundle initial via code splitting.
- Éviter le prop drilling via Redux ou Context API.
- Abstraire les services de données pour testabilité et maintenance.
[directives]
- Mettre en place React.lazy et Suspense pour le chargement différé des vues/segments.
- Centraliser létat global via Redux ou Context API.
- Isoler les appels « data » derrière une couche dabstraction à interface stable.
- Interdire lajout dexemples front dans la base de code.
[validations]
- Vérifier que les points dentrée sont minimes et que les segments non critiques sont chargés à la demande.
- Sassurer que docs/ARCHITECTURE.md décrit les décisions et les points dextension.
[artefacts concernés]
- docs/ARCHITECTURE.md, docs/TESTING.md.
# Architecture frontend
[portée]
Qualité du bundle, découpage, état global et couche de services.
[objectifs]
- Réduire la taille du bundle initial via code splitting.
- Éviter le prop drilling via Redux ou Context API.
- Abstraire les services de données pour testabilité et maintenance.
[directives]
- Mettre en place React.lazy et Suspense pour le chargement différé des vues/segments.
- Centraliser létat global via Redux ou Context API.
- Isoler les appels « data » derrière une couche dabstraction à interface stable.
- Interdire lajout dexemples front dans la base de code.
[validations]
- Vérifier que les points dentrée sont minimes et que les segments non critiques sont chargés à la demande.
- Sassurer que docs/ARCHITECTURE.md décrit les décisions et les points dextension.
[artefacts concernés]
- docs/ARCHITECTURE.md, docs/TESTING.md.

View File

@ -0,0 +1,53 @@
---
alwaysApply: false
---
# Versionnage et publication
[portée]
Gestion sémantique des versions, CHANGELOG, confirmation push/tag.
[objectifs]
- Tenir CHANGELOG.md comme source unique de vérité.
- Demander confirmation avant push et tag.
[directives]
- À chaque changement significatif, mettre à jour CHANGELOG.md (ajouts, changements, corrections, ruptures).
- Proposer un bump semver (major/minor/patch) motivé par limpact.
- Avant tout push ou tag, demander confirmation explicite.
[validations]
- Refus si modification sans entrée correspondante dans CHANGELOG.md.
- Cohérence entre CHANGELOG.md, docs/RELEASE_PLAN.md et docs/ROADMAP.md.
[artefacts concernés]
- CHANGELOG.md, docs/RELEASE_PLAN.md, docs/ROADMAP.md.
# Versionnage et publication
[portée]
Gestion sémantique des versions, CHANGELOG, confirmation push/tag.
[objectifs]
- Tenir CHANGELOG.md comme source unique de vérité.
- Demander confirmation avant push et tag.
[directives]
- À chaque changement significatif, mettre à jour CHANGELOG.md (ajouts, changements, corrections, ruptures).
- Proposer un bump semver (major/minor/patch) motivé par limpact.
- Avant tout push ou tag, demander confirmation explicite.
[validations]
- Refus si modification sans entrée correspondante dans CHANGELOG.md.
- Cohérence entre CHANGELOG.md, docs/RELEASE_PLAN.md et docs/ROADMAP.md.
[artefacts concernés]
- CHANGELOG.md, docs/RELEASE_PLAN.md, docs/ROADMAP.md.

View File

@ -0,0 +1,37 @@
---
alwaysApply: true
---
# Garde de release: tests, documentation, compilation, version, changelog, tag
[portée]
Contrôler systématiquement avant push/tag: tests verts, docs mises à jour, build OK, alignement numéro de version ↔ changelog ↔ tag git, mise à jour de déploiement, confirmation utilisateur (latest vs wip).
[objectifs]
- Empêcher toute publication sans vérifications minimales.
- Exiger la cohérence sémantique (VERSION/TEMPLATE_VERSION ↔ CHANGELOG ↔ tag git).
- Demander explicitement « latest » ou « wip » et appliquer la bonne stratégie.
[directives]
- Avant push/tag, exécuter: tests, compilation, lints (si configurés).
- Mettre à jour la documentation et le changelog en conséquence.
- Aligner le fichier de version (VERSION ou TEMPLATE_VERSION), lentrée CHANGELOG et le tag.
- Demander confirmation utilisateur: `latest` (release stable) ou `wip` (travail en cours).
- latest: entrée datée dans CHANGELOG, version stable, tag `vX.Y.Z`.
- wip: suffixe `-wip` recommandé dans version/tag (ex: `vX.Y.Z-wip.N`).
- Mettre à jour le déploiement après publication (si pipeline défini), sinon documenter létape.
[validations]
- Refuser push/tag si:
- tests/compilation échouent,
- CHANGELOG non mis à jour,
- VERSION/TEMPLATE_VERSION absent ou incohérent,
- release type non fourni (ni latest, ni wip).
[artefacts concernés]
- CHANGELOG.md, VERSION ou TEMPLATE_VERSION, docs/**, .gitea/workflows/**, scripts/**.

View File

@ -0,0 +1,59 @@
---
alwaysApply: true
---
# Open source et Gitea
[portée]
Conformité open source, templates Gitea, CI.
[objectifs]
- Préparer chaque projet pour un dépôt Gitea (git.4nkweb.com).
- Maintenir les fichiers de gouvernance et la CI.
[directives]
- Vérifier la présence et lactualité de : LICENSE, CONTRIBUTING.md, CODE_OF_CONDUCT.md, OPEN_SOURCE_CHECKLIST.md.
- Maintenir .gitea/ :
- ISSUE_TEMPLATE/bug_report.md, feature_request.md
- PULL_REQUEST_TEMPLATE.md
- workflows/ci.yml
- Documenter dans docs/GITEA_SETUP.md la configuration distante et les permissions.
[validations]
- Refus si un des fichiers « gouvernance/CI » manque.
- Cohérence entre docs/OPEN_SOURCE_CHECKLIST.md et létat du repo.
[artefacts concernés]
- .gitea/**, docs/GITEA_SETUP.md, docs/OPEN_SOURCE_CHECKLIST.md.
# Open source et Gitea
[portée]
Conformité open source, templates Gitea, CI.
[objectifs]
- Préparer chaque projet pour un dépôt Gitea (git.4nkweb.com).
- Maintenir les fichiers de gouvernance et la CI.
[directives]
- Vérifier la présence et lactualité de : LICENSE, CONTRIBUTING.md, CODE_OF_CONDUCT.md, OPEN_SOURCE_CHECKLIST.md.
- Maintenir .gitea/ :
- ISSUE_TEMPLATE/bug_report.md, feature_request.md
- PULL_REQUEST_TEMPLATE.md
- workflows/ci.yml
- Documenter dans docs/GITEA_SETUP.md la configuration distante et les permissions.
[validations]
- Refus si un des fichiers « gouvernance/CI » manque.
- Cohérence entre docs/OPEN_SOURCE_CHECKLIST.md et létat du repo.
[artefacts concernés]
- .gitea/**, docs/GITEA_SETUP.md, docs/OPEN_SOURCE_CHECKLIST.md.

View File

@ -0,0 +1,53 @@
---
alwaysApply: true
---
# Tri, diagnostic et résolution de problèmes
[portée]
Boucle de triage : reproduction, diagnostic, correctif, non-régression.
[objectifs]
- Exécuter automatiquement les étapes de résolution.
- Bloquer lavancement tant que les erreurs ne sont pas corrigées.
[directives]
- Étapes obligatoires : reproduction minimale, inspection des logs, bissection des changements, formulation dhypothèses, tests ciblés, correctif, test de non-régression.
- Lorsque plusieurs hypothèses ont été testées, produire un REX dans archive/ avec liens vers les commits.
- Poser des questions de cohérence fonctionnelle si des ambiguïtés subsistent (contrats dAPI, invariants, SLA).
[validations]
- Interdiction de clore une tâche si un test échoue ou si une alerte critique subsiste.
- Traçabilité du REX si investigations multiples.
[artefacts concernés]
- tests/**, archive/**, docs/TESTING.md, docs/ARCHITECTURE.md.
# Tri, diagnostic et résolution de problèmes
[portée]
Boucle de triage : reproduction, diagnostic, correctif, non-régression.
[objectifs]
- Exécuter automatiquement les étapes de résolution.
- Bloquer lavancement tant que les erreurs ne sont pas corrigées.
[directives]
- Étapes obligatoires : reproduction minimale, inspection des logs, bissection des changements, formulation dhypothèses, tests ciblés, correctif, test de non-régression.
- Lorsque plusieurs hypothèses ont été testées, produire un REX dans archive/ avec liens vers les commits.
- Poser des questions de cohérence fonctionnelle si des ambiguïtés subsistent (contrats dAPI, invariants, SLA).
[validations]
- Interdiction de clore une tâche si un test échoue ou si une alerte critique subsiste.
- Traçabilité du REX si investigations multiples.
[artefacts concernés]
- tests/**, archive/**, docs/TESTING.md, docs/ARCHITECTURE.md.

View File

@ -0,0 +1,16 @@
# Index des règles .cursor/rules
- 00-foundations.mdc : règles linguistiques et éditoriales (français, pas dexemples en base, introduction/conclusion).
- 10-project-structure.mdc : arborescence canonique 4NK_node et garde-fous.
- 20-documentation.mdc : documentation continue, remplacement de « RESUME », INDEX.md.
- 30-testing.mdc : tests (unit, integration, connectivity, performance, external), logs/reports.
- 40-dependencies-and-build.mdc : dépendances, compilation, corrections bloquantes.
- 50-data-csv-models.mdc : CSV avec en-têtes multi-lignes, définition des colonnes.
- 60-office-docs.mdc : lecture .docx via docx2txt + repli.
- 70-frontend-architecture.mdc : React.lazy/Suspense, état global, couche de services.
- 80-versioning-and-release.mdc : CHANGELOG, semver, confirmation push/tag.
- 85-release-guard.mdc : garde de release (tests/doc/build/version/changelog/tag; latest vs wip).
- 90-gitea-and-oss.mdc : fichiers open source, .gitea, CI, Gitea remote.
- 95-triage-and-problem-solving.mdc : boucle de diagnostic, REX, non-régression.
Ces règles sont conçues pour être ajoutées au contexte de Cursor depuis linterface (@Cursor Rules) et sappuient sur le mécanisme de règles projet stockées dans `.cursor/rules/`. :contentReference[oaicite:3]{index=3}

24
.cursorignore Normal file
View File

@ -0,0 +1,24 @@
node_modules/
dist/
build/
coverage/
.cache/
.turbo/
.parcel-cache/
assets/ihm/
web/ihm/
logs/
*.log
**/*.map
**/*.min.*
**/*.wasm
**/*.png
**/*.jpg
**/*.jpeg
**/*.svg
**/*.ico
**/*.pdf
!.cursor/
!AGENTS.md

View File

@ -0,0 +1,97 @@
---
name: Bug Report
about: Signaler un bug pour nous aider à améliorer 4NK Node
title: '[BUG] '
labels: ['bug', 'needs-triage']
assignees: ''
---
## 🐛 Description du Bug
Description claire et concise du problème.
## 🔄 Étapes pour Reproduire
1. Aller à '...'
2. Cliquer sur '...'
3. Faire défiler jusqu'à '...'
4. Voir l'erreur
## ✅ Comportement Attendu
Description de ce qui devrait se passer.
## ❌ Comportement Actuel
Description de ce qui se passe actuellement.
## 📸 Capture d'Écran
Si applicable, ajoutez une capture d'écran pour expliquer votre problème.
## 💻 Informations Système
- **OS** : [ex: Ubuntu 20.04, macOS 12.0, Windows 11]
- **Docker** : [ex: 20.10.0]
- **Docker Compose** : [ex: 2.0.0]
- **Version 4NK Node** : [ex: v1.0.0]
- **Architecture** : [ex: x86_64, ARM64]
## 📋 Configuration
### Services Actifs
```bash
docker ps
```
### Variables d'Environnement
```bash
# Bitcoin Core
BITCOIN_NETWORK=signet
BITCOIN_RPC_PORT=18443
# Blindbit
BLINDBIT_PORT=8000
# SDK Relay
SDK_RELAY_PORTS=8090-8095
```
## 📝 Logs
### Logs Pertinents
```
Logs pertinents ici
```
### Logs d'Erreur
```
Logs d'erreur ici
```
### Logs de Debug
```
Logs de debug ici (si RUST_LOG=debug)
```
## 🔧 Tentatives de Résolution
- [ ] Redémarrage des services
- [ ] Nettoyage des volumes Docker
- [ ] Vérification de la connectivité réseau
- [ ] Mise à jour des dépendances
- [ ] Vérification de la configuration
## 📚 Contexte Supplémentaire
Toute autre information pertinente sur le problème.
## 🔗 Liens Utiles
- [Documentation](docs/)
- [Guide de Dépannage](docs/TROUBLESHOOTING.md)
- [Issues Similaires](https://git.4nkweb.com/4nk/4NK_node/issues?q=is%3Aissue+is%3Aopen+label%3Abug)
---
**Merci de votre contribution !** 🙏

View File

@ -0,0 +1,156 @@
---
name: Feature Request
about: Proposer une nouvelle fonctionnalité pour 4NK Node
title: '[FEATURE] '
labels: ['enhancement', 'needs-triage']
assignees: ''
---
## 🚀 Résumé
Description claire et concise de la fonctionnalité souhaitée.
## 💡 Motivation
Pourquoi cette fonctionnalité est-elle nécessaire ? Quels problèmes résout-elle ?
### Problèmes Actuels
- Problème 1
- Problème 2
- Problème 3
### Avantages de la Solution
- Avantage 1
- Avantage 2
- Avantage 3
## 🎯 Proposition
Description détaillée de la fonctionnalité proposée.
### Fonctionnalités Principales
- [ ] Fonctionnalité 1
- [ ] Fonctionnalité 2
- [ ] Fonctionnalité 3
### Interface Utilisateur
Description de l'interface utilisateur si applicable.
### API Changes
Description des changements d'API si applicable.
## 🔄 Alternatives Considérées
Autres solutions envisagées et pourquoi elles n'ont pas été choisies.
### Alternative 1
- **Description** : ...
- **Pourquoi rejetée** : ...
### Alternative 2
- **Description** : ...
- **Pourquoi rejetée** : ...
## 📊 Impact
### Impact sur les Utilisateurs
- Impact positif 1
- Impact positif 2
- Impact négatif potentiel (si applicable)
### Impact sur l'Architecture
- Changements nécessaires
- Compatibilité avec l'existant
- Performance
### Impact sur la Maintenance
- Complexité ajoutée
- Tests nécessaires
- Documentation requise
## 💻 Exemples d'Utilisation
### Cas d'Usage 1
```bash
# Exemple de commande ou configuration
```
### Cas d'Usage 2
```python
# Exemple de code Python
```
### Cas d'Usage 3
```javascript
// Exemple de code JavaScript
```
## 🧪 Tests
### Tests Nécessaires
- [ ] Tests unitaires
- [ ] Tests d'intégration
- [ ] Tests de performance
- [ ] Tests de sécurité
- [ ] Tests de compatibilité
### Scénarios de Test
- Scénario 1
- Scénario 2
- Scénario 3
## 📚 Documentation
### Documentation Requise
- [ ] Guide d'utilisation
- [ ] Documentation API
- [ ] Exemples de code
- [ ] Guide de migration
- [ ] FAQ
## 🔧 Implémentation
### Étapes Proposées
1. **Phase 1** : [Description]
2. **Phase 2** : [Description]
3. **Phase 3** : [Description]
### Estimation de Temps
- **Développement** : X jours/semaines
- **Tests** : X jours/semaines
- **Documentation** : X jours/semaines
- **Total** : X jours/semaines
### Ressources Nécessaires
- Développeur(s)
- Testeur(s)
- Documentateur(s)
- Infrastructure
## 🎯 Critères de Succès
Comment mesurer le succès de cette fonctionnalité ?
- [ ] Critère 1
- [ ] Critère 2
- [ ] Critère 3
## 🔗 Liens Utiles
- [Documentation existante](docs/)
- [Issues similaires](https://git.4nkweb.com/4nk/4NK_node/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement)
- [Roadmap](https://git.4nkweb.com/4nk/4NK_node/projects)
- [Discussions](https://git.4nkweb.com/4nk/4NK_node/issues)
## 📋 Checklist
- [ ] J'ai vérifié que cette fonctionnalité n'existe pas déjà
- [ ] J'ai lu la documentation existante
- [ ] J'ai vérifié les issues similaires
- [ ] J'ai fourni des exemples d'utilisation
- [ ] J'ai considéré l'impact sur l'existant
- [ ] J'ai proposé des tests
---
**Merci de votre contribution à l'amélioration de 4NK Node !** 🌟

View File

@ -0,0 +1,180 @@
# Pull Request - 4NK Node
## 📋 Description
Description claire et concise des changements apportés.
### Type de Changement
- [ ] 🐛 Bug fix
- [ ] ✨ Nouvelle fonctionnalité
- [ ] 📚 Documentation
- [ ] 🧪 Tests
- [ ] 🔧 Refactoring
- [ ] 🚀 Performance
- [ ] 🔒 Sécurité
- [ ] 🎨 Style/UI
- [ ] 🏗️ Architecture
- [ ] 📦 Build/CI
### Composants Affectés
- [ ] Bitcoin Core
- [ ] Blindbit
- [ ] SDK Relay
- [ ] Tor
- [ ] Docker/Infrastructure
- [ ] Tests
- [ ] Documentation
- [ ] Scripts
## 🔗 Issue(s) Liée(s)
Fixes #(issue)
Relates to #(issue)
## 🧪 Tests
### Tests Exécutés
- [ ] Tests unitaires
- [ ] Tests d'intégration
- [ ] Tests de connectivité
- [ ] Tests externes
- [ ] Tests de performance
### Commandes de Test
```bash
# Tests complets
./tests/run_all_tests.sh
# Tests spécifiques
./tests/run_unit_tests.sh
./tests/run_integration_tests.sh
```
### Résultats des Tests
```
Résultats des tests ici
```
## 📸 Captures d'Écran
Si applicable, ajoutez des captures d'écran pour les changements visuels.
## 🔧 Changements Techniques
### Fichiers Modifiés
- `fichier1.rs` - Description des changements
- `fichier2.py` - Description des changements
- `docker-compose.yml` - Description des changements
### Nouveaux Fichiers
- `nouveau_fichier.rs` - Description
- `nouveau_script.sh` - Description
### Fichiers Supprimés
- `ancien_fichier.rs` - Raison de la suppression
### Changements de Configuration
```yaml
# Exemple de changement de configuration
service:
new_option: value
```
## 📚 Documentation
### Documentation Mise à Jour
- [ ] README.md
- [ ] docs/INSTALLATION.md
- [ ] docs/USAGE.md
- [ ] docs/API.md
- [ ] docs/ARCHITECTURE.md
### Nouvelle Documentation
- [ ] Nouveau guide créé
- [ ] Exemples ajoutés
- [ ] API documentée
## 🔍 Code Review Checklist
### Code Quality
- [ ] Le code suit les standards du projet
- [ ] Les noms de variables/fonctions sont clairs
- [ ] Les commentaires sont appropriés
- [ ] Pas de code mort ou commenté
- [ ] Gestion d'erreurs appropriée
### Performance
- [ ] Pas de régression de performance
- [ ] Optimisations appliquées si nécessaire
- [ ] Tests de performance ajoutés
### Sécurité
- [ ] Pas de vulnérabilités introduites
- [ ] Validation des entrées utilisateur
- [ ] Gestion sécurisée des secrets
### Tests
- [ ] Couverture de tests suffisante
- [ ] Tests pour les cas d'erreur
- [ ] Tests d'intégration si nécessaire
### Documentation
- [ ] Code auto-documenté
- [ ] Documentation mise à jour
- [ ] Exemples fournis
## 🚀 Déploiement
### Impact sur le Déploiement
- [ ] Aucun impact
- [ ] Migration de données requise
- [ ] Changement de configuration
- [ ] Redémarrage des services
### Étapes de Déploiement
```bash
# Étapes pour déployer les changements
```
## 📊 Métriques
### Impact sur les Performances
- Temps de réponse : +/- X%
- Utilisation mémoire : +/- X%
- Utilisation CPU : +/- X%
### Impact sur la Stabilité
- Taux d'erreur : +/- X%
- Disponibilité : +/- X%
## 🔄 Compatibilité
### Compatibilité Ascendante
- [ ] Compatible avec les versions précédentes
- [ ] Migration automatique
- [ ] Migration manuelle requise
### Compatibilité Descendante
- [ ] Compatible avec les futures versions
- [ ] API stable
- [ ] Configuration stable
## 🎯 Critères de Succès
- [ ] Critère 1
- [ ] Critère 2
- [ ] Critère 3
## 📝 Notes Supplémentaires
Informations supplémentaires importantes pour les reviewers.
## 🔗 Liens Utiles
- [Documentation](docs/)
- [Tests](tests/)
- [Issues liées](https://git.4nkweb.com/4nk/4NK_node/issues)
---
**Merci pour votre contribution !** 🙏

View File

@ -0,0 +1,14 @@
# LOCAL_OVERRIDES.yml — dérogations locales contrôlées
overrides:
- path: ".gitea/workflows/ci.yml"
reason: "spécificité denvironnement"
owner: "@maintainer_handle"
expires: "2025-12-31"
- path: "scripts/auto-ssh-push.sh"
reason: "flux particulier temporaire"
owner: "@maintainer_handle"
expires: "2025-10-01"
policy:
allow_only_listed_paths: true
require_expiry: true
audit_in_ci: true

345
.gitea/workflows/ci.yml Normal file
View File

@ -0,0 +1,345 @@
name: CI - 4NK Node
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
env:
RUST_VERSION: '1.70'
DOCKER_COMPOSE_VERSION: '2.20.0'
jobs:
# Job de vérification du code
code-quality:
name: Code Quality
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Rust
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.RUST_VERSION }}
override: true
- name: Cache Rust dependencies
uses: actions/cache@v3
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Run clippy
run: |
cd sdk_relay
cargo clippy --all-targets --all-features -- -D warnings
- name: Run rustfmt
run: |
cd sdk_relay
cargo fmt --all -- --check
- name: Check documentation
run: |
cd sdk_relay
cargo doc --no-deps
- name: Check for TODO/FIXME
run: |
if grep -r "TODO\|FIXME" . --exclude-dir=.git --exclude-dir=target; then
echo "Found TODO/FIXME comments. Please address them."
exit 1
fi
# Job de tests unitaires
unit-tests:
name: Unit Tests
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Rust
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.RUST_VERSION }}
override: true
- name: Cache Rust dependencies
uses: actions/cache@v3
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Run unit tests
run: |
cd sdk_relay
cargo test --lib --bins
- name: Run integration tests
run: |
cd sdk_relay
cargo test --tests
# Job de tests d'intégration
integration-tests:
name: Integration Tests
runs-on: ubuntu-latest
services:
docker:
image: docker:24.0.5
options: >-
--health-cmd "docker info"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 2375:2375
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Docker images
run: |
docker build -t 4nk-node-bitcoin ./bitcoin
docker build -t 4nk-node-blindbit ./blindbit
docker build -t 4nk-node-sdk-relay -f ./sdk_relay/Dockerfile ..
- name: Run integration tests
run: |
# Tests de connectivité de base
./tests/run_connectivity_tests.sh || true
# Tests d'intégration
./tests/run_integration_tests.sh || true
- name: Upload test results
uses: actions/upload-artifact@v3
if: always()
with:
name: test-results
path: |
tests/logs/
tests/reports/
retention-days: 7
# Job de tests de sécurité
security-tests:
name: Security Tests
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Rust
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.RUST_VERSION }}
override: true
- name: Run cargo audit
run: |
cd sdk_relay
cargo audit --deny warnings
- name: Check for secrets
run: |
# Vérifier les secrets potentiels
if grep -r "password\|secret\|key\|token" . --exclude-dir=.git --exclude-dir=target --exclude=*.md; then
echo "Potential secrets found. Please review."
exit 1
fi
- name: Check file permissions
run: |
# Vérifier les permissions sensibles
find . -type f -perm /0111 -name "*.conf" -o -name "*.key" -o -name "*.pem" | while read file; do
if [[ $(stat -c %a "$file") != "600" ]]; then
echo "Warning: $file has insecure permissions"
fi
done
# Job de build et test Docker
docker-build:
name: Docker Build & Test
runs-on: ubuntu-latest
services:
docker:
image: docker:24.0.5
options: >-
--health-cmd "docker info"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 2375:2375
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and test Bitcoin Core
run: |
docker build -t 4nk-node-bitcoin:test ./bitcoin
docker run --rm 4nk-node-bitcoin:test bitcoin-cli --version
- name: Build and test Blindbit
run: |
docker build -t 4nk-node-blindbit:test ./blindbit
docker run --rm 4nk-node-blindbit:test --version || true
- name: Build and test SDK Relay
run: |
docker build -t 4nk-node-sdk-relay:test -f ./sdk_relay/Dockerfile ..
docker run --rm 4nk-node-sdk-relay:test --version || true
- name: Test Docker Compose
run: |
docker-compose config
docker-compose build --no-cache
# Job de tests de documentation
documentation-tests:
name: Documentation Tests
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Check markdown links
run: |
# Vérification basique des liens markdown
find . -name "*.md" -exec grep -l "\[.*\](" {} \; | while read file; do
echo "Checking links in $file"
done
- name: Check documentation structure
run: |
# Vérifier la présence des fichiers de documentation essentiels
required_files=(
"README.md"
"LICENSE"
"CONTRIBUTING.md"
"CHANGELOG.md"
"CODE_OF_CONDUCT.md"
"SECURITY.md"
"docs/INDEX.md"
"docs/INSTALLATION.md"
"docs/USAGE.md"
)
for file in "${required_files[@]}"; do
if [[ ! -f "$file" ]]; then
echo "Missing required documentation file: $file"
exit 1
fi
done
- name: Validate documentation
run: |
# Vérifier la cohérence de la documentation
if ! grep -q "4NK Node" README.md; then
echo "README.md should mention '4NK Node'"
exit 1
fi
# Job de release guard (cohérence release)
release-guard:
name: Release Guard
runs-on: ubuntu-latest
needs: [code-quality, unit-tests, documentation-tests]
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Ensure guard scripts are executable
run: |
chmod +x scripts/release/guard.sh || true
chmod +x scripts/checks/version_alignment.sh || true
- name: Version alignment check
run: |
if [ -f scripts/checks/version_alignment.sh ]; then
./scripts/checks/version_alignment.sh
else
echo "No version alignment script (ok)"
fi
- name: Release guard (CI verify)
env:
RELEASE_TYPE: ci-verify
run: |
if [ -f scripts/release/guard.sh ]; then
./scripts/release/guard.sh
else
echo "No guard script (ok)"
fi
# Job de tests de performance
performance-tests:
name: Performance Tests
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Rust
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.RUST_VERSION }}
override: true
- name: Run performance tests
run: |
cd sdk_relay
cargo test --release --test performance_tests || true
- name: Check memory usage
run: |
# Tests de base de consommation mémoire
echo "Performance tests completed"
# Job de notification
notify:
name: Notify
runs-on: ubuntu-latest
needs: [code-quality, unit-tests, integration-tests, security-tests, docker-build, documentation-tests]
if: always()
steps:
- name: Notify success
if: needs.code-quality.result == 'success' && needs.unit-tests.result == 'success' && needs.integration-tests.result == 'success' && needs.security-tests.result == 'success' && needs.docker-build.result == 'success' && needs.documentation-tests.result == 'success'
run: |
echo "✅ All tests passed successfully!"
- name: Notify failure
if: needs.code-quality.result == 'failure' || needs.unit-tests.result == 'failure' || needs.integration-tests.result == 'failure' || needs.security-tests.result == 'failure' || needs.docker-build.result == 'failure' || needs.documentation-tests.result == 'failure'
run: |
echo "❌ Some tests failed!"
exit 1

View File

@ -0,0 +1,39 @@
# .gitea/workflows/template-sync.yml — synchronisation et contrôles dintégrité
name: 4NK Template Sync
on:
schedule: # planification régulière
- cron: "0 4 * * 1" # exécution hebdomadaire (UTC)
workflow_dispatch: {} # déclenchement manuel
jobs:
check-and-sync:
runs-on: linux
steps:
- name: Lire TEMPLATE_VERSION et .4nk-sync.yml
# Doit charger ref courant, source_repo et périmètre paths
- name: Récupérer la version publiée du template/4NK_rules
# Doit comparer TEMPLATE_VERSION avec ref amont
- name: Créer branche de synchronisation si divergence
# Doit créer chore/template-sync-<date> et préparer un commit
- name: Synchroniser les chemins autoritatifs
# Doit mettre à jour .cursor/**, .gitea/**, AGENTS.md, scripts/**, docs/SSH_UPDATE.md
- name: Contrôles post-sync (bloquants)
# 1) Vérifier présence et exécutable des scripts/*.sh
# 2) Vérifier mise à jour CHANGELOG.md et docs/INDEX.md
# 3) Vérifier docs/SSH_UPDATE.md si scripts/** a changé
# 4) Vérifier absence de secrets en clair dans scripts/**
# 5) Vérifier manifest_checksum si publié
- name: Tests, lint, sécurité statique
# Doit exiger un état vert
- name: Ouvrir PR de synchronisation
# Titre: "[template-sync] chore: aligner .cursor/.gitea/AGENTS.md/scripts"
# Doit inclure résumé des fichiers modifiés et la version appliquée
- name: Mettre à jour TEMPLATE_VERSION (dans PR)
# Doit remplacer la valeur par la ref appliquée

View File

@ -0,0 +1,99 @@
---
name: Bug Report
about: Signaler un bug pour nous aider à améliorer 4NK Node
title: '[BUG] '
labels: ['bug', 'needs-triage']
assignees: ''
---
> Ce fichier est un modèle (template). Adaptez les champs à votre projet dérivé.
## 🐛 Description du Bug
Description claire et concise du problème.
## 🔄 Étapes pour Reproduire
1. Aller à '...'
2. Cliquer sur '...'
3. Faire défiler jusqu'à '...'
4. Voir l'erreur
## ✅ Comportement Attendu
Description de ce qui devrait se passer.
## ❌ Comportement Actuel
Description de ce qui se passe actuellement.
## 📸 Capture d'Écran
Si applicable, ajoutez une capture d'écran pour expliquer votre problème.
## 💻 Informations Système
- **OS** : [ex: Ubuntu 20.04, macOS 12.0, Windows 11]
- **Docker** : [ex: 20.10.0]
- **Docker Compose** : [ex: 2.0.0]
- **Version 4NK Node** : [ex: v1.0.0]
- **Architecture** : [ex: x86_64, ARM64]
## 📋 Configuration
### Services Actifs
```bash
docker ps
```
### Variables d'Environnement
```bash
# Bitcoin Core
BITCOIN_NETWORK=signet
BITCOIN_RPC_PORT=18443
# Blindbit
BLINDBIT_PORT=8000
# SDK Relay
SDK_RELAY_PORTS=8090-8095
```
## 📝 Logs
### Logs Pertinents
```
Logs pertinents ici
```
### Logs d'Erreur
```
Logs d'erreur ici
```
### Logs de Debug
```
Logs de debug ici (si RUST_LOG=debug)
```
## 🔧 Tentatives de Résolution
- [ ] Redémarrage des services
- [ ] Nettoyage des volumes Docker
- [ ] Vérification de la connectivité réseau
- [ ] Mise à jour des dépendances
- [ ] Vérification de la configuration
## 📚 Contexte Supplémentaire
Toute autre information pertinente sur le problème.
## 🔗 Liens Utiles
- [Documentation](docs/)
- [Guide de Dépannage](docs/TROUBLESHOOTING.md)
- [Issues Similaires](https://git.4nkweb.com/4nk/4NK_node/issues?q=is%3Aissue+is%3Aopen+label%3Abug)
---
**Merci de votre contribution !** 🙏

View File

@ -0,0 +1,158 @@
---
name: Feature Request
about: Proposer une nouvelle fonctionnalité pour 4NK Node
title: '[FEATURE] '
labels: ['enhancement', 'needs-triage']
assignees: ''
---
> Ce fichier est un modèle (template). Adaptez les champs à votre projet dérivé.
## 🚀 Résumé
Description claire et concise de la fonctionnalité souhaitée.
## 💡 Motivation
Pourquoi cette fonctionnalité est-elle nécessaire ? Quels problèmes résout-elle ?
### Problèmes Actuels
- Problème 1
- Problème 2
- Problème 3
### Avantages de la Solution
- Avantage 1
- Avantage 2
- Avantage 3
## 🎯 Proposition
Description détaillée de la fonctionnalité proposée.
### Fonctionnalités Principales
- [ ] Fonctionnalité 1
- [ ] Fonctionnalité 2
- [ ] Fonctionnalité 3
### Interface Utilisateur
Description de l'interface utilisateur si applicable.
### API Changes
Description des changements d'API si applicable.
## 🔄 Alternatives Considérées
Autres solutions envisagées et pourquoi elles n'ont pas été choisies.
### Alternative 1
- **Description** : ...
- **Pourquoi rejetée** : ...
### Alternative 2
- **Description** : ...
- **Pourquoi rejetée** : ...
## 📊 Impact
### Impact sur les Utilisateurs
- Impact positif 1
- Impact positif 2
- Impact négatif potentiel (si applicable)
### Impact sur l'Architecture
- Changements nécessaires
- Compatibilité avec l'existant
- Performance
### Impact sur la Maintenance
- Complexité ajoutée
- Tests nécessaires
- Documentation requise
## 💻 Exemples d'Utilisation
### Cas d'Usage 1
```bash
# Exemple de commande ou configuration
```
### Cas d'Usage 2
```python
# Exemple de code Python
```
### Cas d'Usage 3
```javascript
// Exemple de code JavaScript
```
## 🧪 Tests
### Tests Nécessaires
- [ ] Tests unitaires
- [ ] Tests d'intégration
- [ ] Tests de performance
- [ ] Tests de sécurité
- [ ] Tests de compatibilité
### Scénarios de Test
- Scénario 1
- Scénario 2
- Scénario 3
## 📚 Documentation
### Documentation Requise
- [ ] Guide d'utilisation
- [ ] Documentation API
- [ ] Exemples de code
- [ ] Guide de migration
- [ ] FAQ
## 🔧 Implémentation
### Étapes Proposées
1. **Phase 1** : [Description]
2. **Phase 2** : [Description]
3. **Phase 3** : [Description]
### Estimation de Temps
- **Développement** : X jours/semaines
- **Tests** : X jours/semaines
- **Documentation** : X jours/semaines
- **Total** : X jours/semaines
### Ressources Nécessaires
- Développeur(s)
- Testeur(s)
- Documentateur(s)
- Infrastructure
## 🎯 Critères de Succès
Comment mesurer le succès de cette fonctionnalité ?
- [ ] Critère 1
- [ ] Critère 2
- [ ] Critère 3
## 🔗 Liens Utiles
- [Documentation existante](docs/)
- [Issues similaires](https://git.4nkweb.com/4nk/4NK_node/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement)
- [Roadmap](https://git.4nkweb.com/4nk/4NK_node/projects)
- [Discussions](https://git.4nkweb.com/4nk/4NK_node/issues)
## 📋 Checklist
- [ ] J'ai vérifié que cette fonctionnalité n'existe pas déjà
- [ ] J'ai lu la documentation existante
- [ ] J'ai vérifié les issues similaires
- [ ] J'ai fourni des exemples d'utilisation
- [ ] J'ai considéré l'impact sur l'existant
- [ ] J'ai proposé des tests
---
**Merci de votre contribution à l'amélioration de 4NK Node !** 🌟

View File

@ -0,0 +1,183 @@
# Pull Request - 4NK Node
> Ce fichier est un modèle PR. Adaptez les sections à votre projet dérivé.
## 📋 Description
Description claire et concise des changements apportés.
### Type de Changement
- [ ] 🐛 Bug fix
- [ ] ✨ Nouvelle fonctionnalité
- [ ] 📚 Documentation
- [ ] 🧪 Tests
- [ ] 🔧 Refactoring
- [ ] 🚀 Performance
- [ ] 🔒 Sécurité
- [ ] 🎨 Style/UI
- [ ] 🏗️ Architecture
- [ ] 📦 Build/CI
### Composants Affectés
- [ ] Bitcoin Core
- [ ] Blindbit
- [ ] SDK Relay
- [ ] Tor
- [ ] Docker/Infrastructure
- [ ] Tests
- [ ] Documentation
- [ ] Scripts
## 🔗 Issue(s) Liée(s)
Fixes #(issue)
Relates to #(issue)
## 🧪 Tests
### Tests Exécutés
- [ ] Tests unitaires
- [ ] Tests d'intégration
- [ ] Tests de connectivité
- [ ] Tests externes
- [ ] Tests de performance
- [ ] Release Guard local (`RELEASE_TYPE=ci-verify scripts/release/guard.sh`)
### Commandes de Test
```bash
# Tests complets
./tests/run_all_tests.sh
# Tests spécifiques
./tests/run_unit_tests.sh
./tests/run_integration_tests.sh
```
### Résultats des Tests
```
Résultats des tests ici
```
## 📸 Captures d'Écran
Si applicable, ajoutez des captures d'écran pour les changements visuels.
## 🔧 Changements Techniques
### Fichiers Modifiés
- `fichier1.rs` - Description des changements
- `fichier2.py` - Description des changements
- `docker-compose.yml` - Description des changements
### Nouveaux Fichiers
- `nouveau_fichier.rs` - Description
- `nouveau_script.sh` - Description
### Fichiers Supprimés
- `ancien_fichier.rs` - Raison de la suppression
### Changements de Configuration
```yaml
# Exemple de changement de configuration
service:
new_option: value
```
## 📚 Documentation
### Documentation Mise à Jour
- [ ] README.md
- [ ] docs/INSTALLATION.md
- [ ] docs/USAGE.md
- [ ] docs/API.md
- [ ] docs/ARCHITECTURE.md
### Nouvelle Documentation
- [ ] Nouveau guide créé
- [ ] Exemples ajoutés
- [ ] API documentée
## 🔍 Code Review Checklist
### Code Quality
- [ ] Le code suit les standards du projet
- [ ] Les noms de variables/fonctions sont clairs
- [ ] Les commentaires sont appropriés
- [ ] Pas de code mort ou commenté
- [ ] Gestion d'erreurs appropriée
### Performance
- [ ] Pas de régression de performance
- [ ] Optimisations appliquées si nécessaire
- [ ] Tests de performance ajoutés
### Sécurité
- [ ] Pas de vulnérabilités introduites
- [ ] Validation des entrées utilisateur
- [ ] Gestion sécurisée des secrets
### Tests
- [ ] Couverture de tests suffisante
- [ ] Tests pour les cas d'erreur
- [ ] Tests d'intégration si nécessaire
### Documentation
- [ ] Code auto-documenté
- [ ] Documentation mise à jour
- [ ] Exemples fournis
## 🚀 Déploiement
### Impact sur le Déploiement
- [ ] Aucun impact
- [ ] Migration de données requise
- [ ] Changement de configuration
- [ ] Redémarrage des services
### Étapes de Déploiement
```bash
# Étapes pour déployer les changements
```
## 📊 Métriques
### Impact sur les Performances
- Temps de réponse : +/- X%
- Utilisation mémoire : +/- X%
- Utilisation CPU : +/- X%
### Impact sur la Stabilité
- Taux d'erreur : +/- X%
- Disponibilité : +/- X%
## 🔄 Compatibilité
### Compatibilité Ascendante
- [ ] Compatible avec les versions précédentes
- [ ] Migration automatique
- [ ] Migration manuelle requise
### Compatibilité Descendante
- [ ] Compatible avec les futures versions
- [ ] API stable
- [ ] Configuration stable
## 🎯 Critères de Succès
- [ ] Critère 1
- [ ] Critère 2
- [ ] Critère 3
## 📝 Notes Supplémentaires
Informations supplémentaires importantes pour les reviewers.
## 🔗 Liens Utiles
- [Documentation](docs/)
- [Tests](tests/)
- [Issues liées](https://git.4nkweb.com/4nk/4NK_node/issues)
---
**Merci pour votre contribution !** 🙏

View File

@ -0,0 +1,14 @@
# LOCAL_OVERRIDES.yml — dérogations locales contrôlées (fichier modèle)
overrides:
- path: ".gitea/workflows/ci.yml"
reason: "spécificité denvironnement"
owner: "@maintainer_handle"
expires: "2025-12-31"
- path: "scripts/scripts/auto-ssh-push.sh"
reason: "flux particulier temporaire"
owner: "@maintainer_handle"
expires: "2025-10-01"
policy:
allow_only_listed_paths: true
require_expiry: true
audit_in_ci: true

View File

@ -0,0 +1,346 @@
# Template CI - 4NK Node (ce fichier est un modèle, adaptez selon votre projet)
name: CI - 4NK Node
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
env:
RUST_VERSION: '1.70'
DOCKER_COMPOSE_VERSION: '2.20.0'
jobs:
# Job de vérification du code
code-quality:
name: Code Quality
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Rust
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.RUST_VERSION }}
override: true
- name: Cache Rust dependencies
uses: actions/cache@v3
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Run clippy
run: |
cd sdk_relay
cargo clippy --all-targets --all-features -- -D warnings
- name: Run rustfmt
run: |
cd sdk_relay
cargo fmt --all -- --check
- name: Check documentation
run: |
cd sdk_relay
cargo doc --no-deps
- name: Check for TODO/FIXME
run: |
if grep -r "TODO\|FIXME" . --exclude-dir=.git --exclude-dir=target; then
echo "Found TODO/FIXME comments. Please address them."
exit 1
fi
# Job de tests unitaires
unit-tests:
name: Unit Tests
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Rust
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.RUST_VERSION }}
override: true
- name: Cache Rust dependencies
uses: actions/cache@v3
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Run unit tests
run: |
cd sdk_relay
cargo test --lib --bins
- name: Run integration tests
run: |
cd sdk_relay
cargo test --tests
# Job de tests d'intégration
integration-tests:
name: Integration Tests
runs-on: ubuntu-latest
services:
docker:
image: docker:24.0.5
options: >-
--health-cmd "docker info"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 2375:2375
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Docker images
run: |
docker build -t 4nk-node-bitcoin ./bitcoin
docker build -t 4nk-node-blindbit ./blindbit
docker build -t 4nk-node-sdk-relay -f ./sdk_relay/Dockerfile ..
- name: Run integration tests
run: |
# Tests de connectivité de base
./tests/run_connectivity_tests.sh || true
# Tests d'intégration
./tests/run_integration_tests.sh || true
- name: Upload test results
uses: actions/upload-artifact@v3
if: always()
with:
name: test-results
path: |
tests/logs/
tests/reports/
retention-days: 7
# Job de tests de sécurité
security-tests:
name: Security Tests
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Rust
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.RUST_VERSION }}
override: true
- name: Run cargo audit
run: |
cd sdk_relay
cargo audit --deny warnings
- name: Check for secrets
run: |
# Vérifier les secrets potentiels
if grep -r "password\|secret\|key\|token" . --exclude-dir=.git --exclude-dir=target --exclude=*.md; then
echo "Potential secrets found. Please review."
exit 1
fi
- name: Check file permissions
run: |
# Vérifier les permissions sensibles
find . -type f -perm /0111 -name "*.conf" -o -name "*.key" -o -name "*.pem" | while read file; do
if [[ $(stat -c %a "$file") != "600" ]]; then
echo "Warning: $file has insecure permissions"
fi
done
# Job de build et test Docker
docker-build:
name: Docker Build & Test
runs-on: ubuntu-latest
services:
docker:
image: docker:24.0.5
options: >-
--health-cmd "docker info"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 2375:2375
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and test Bitcoin Core
run: |
docker build -t 4nk-node-bitcoin:test ./bitcoin
docker run --rm 4nk-node-bitcoin:test bitcoin-cli --version
- name: Build and test Blindbit
run: |
docker build -t 4nk-node-blindbit:test ./blindbit
docker run --rm 4nk-node-blindbit:test --version || true
- name: Build and test SDK Relay
run: |
docker build -t 4nk-node-sdk-relay:test -f ./sdk_relay/Dockerfile ..
docker run --rm 4nk-node-sdk-relay:test --version || true
- name: Test Docker Compose
run: |
docker-compose config
docker-compose build --no-cache
# Job de tests de documentation
documentation-tests:
name: Documentation Tests
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Check markdown links
run: |
# Vérification basique des liens markdown
find . -name "*.md" -exec grep -l "\[.*\](" {} \; | while read file; do
echo "Checking links in $file"
done
- name: Check documentation structure
run: |
# Vérifier la présence des fichiers de documentation essentiels
required_files=(
"README.md"
"LICENSE"
"CONTRIBUTING.md"
"CHANGELOG.md"
"CODE_OF_CONDUCT.md"
"SECURITY.md"
"docs/INDEX.md"
"docs/INSTALLATION.md"
"docs/USAGE.md"
)
for file in "${required_files[@]}"; do
if [[ ! -f "$file" ]]; then
echo "Missing required documentation file: $file"
exit 1
fi
done
- name: Validate documentation
run: |
# Vérifier la cohérence de la documentation
if ! grep -q "4NK Node" README.md; then
echo "README.md should mention '4NK Node'"
exit 1
fi
# Job de release guard (cohérence release)
release-guard:
name: Release Guard
runs-on: ubuntu-latest
needs: [code-quality, unit-tests, documentation-tests]
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Ensure guard scripts are executable
run: |
chmod +x scripts/release/guard.sh || true
chmod +x scripts/checks/version_alignment.sh || true
- name: Version alignment check
run: |
if [ -f scripts/checks/version_alignment.sh ]; then
./scripts/checks/version_alignment.sh
else
echo "No version alignment script (ok)"
fi
- name: Release guard (CI verify)
env:
RELEASE_TYPE: ci-verify
run: |
if [ -f scripts/release/guard.sh ]; then
./scripts/release/guard.sh
else
echo "No guard script (ok)"
fi
# Job de tests de performance
performance-tests:
name: Performance Tests
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Rust
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.RUST_VERSION }}
override: true
- name: Run performance tests
run: |
cd sdk_relay
cargo test --release --test performance_tests || true
- name: Check memory usage
run: |
# Tests de base de consommation mémoire
echo "Performance tests completed"
# Job de notification
notify:
name: Notify
runs-on: ubuntu-latest
needs: [code-quality, unit-tests, integration-tests, security-tests, docker-build, documentation-tests]
if: always()
steps:
- name: Notify success
if: needs.code-quality.result == 'success' && needs.unit-tests.result == 'success' && needs.integration-tests.result == 'success' && needs.security-tests.result == 'success' && needs.docker-build.result == 'success' && needs.documentation-tests.result == 'success'
run: |
echo "✅ All tests passed successfully!"
- name: Notify failure
if: needs.code-quality.result == 'failure' || needs.unit-tests.result == 'failure' || needs.integration-tests.result == 'failure' || needs.security-tests.result == 'failure' || needs.docker-build.result == 'failure' || needs.documentation-tests.result == 'failure'
run: |
echo "❌ Some tests failed!"
exit 1

View File

@ -0,0 +1,39 @@
# .gitea/workflows/template-sync.yml — synchronisation et contrôles dintégrité (fichier modèle)
name: 4NK Template Sync
on:
schedule: # planification régulière
- cron: "0 4 * * 1" # exécution hebdomadaire (UTC)
workflow_dispatch: {} # déclenchement manuel
jobs:
check-and-sync:
runs-on: ubuntu-latest
steps:
- name: Lire TEMPLATE_VERSION et .4nk-sync.yml
# Doit charger ref courant, source_repo et périmètre paths
- name: Récupérer la version publiée du template/4NK_rules
# Doit comparer TEMPLATE_VERSION avec ref amont
- name: Créer branche de synchronisation si divergence
# Doit créer chore/template-sync-<date> et préparer un commit
- name: Synchroniser les chemins autoritatifs
# Doit mettre à jour .cursor/**, .gitea/**, AGENTS.md, scripts/**, docs/SSH_UPDATE.md
- name: Contrôles post-sync (bloquants)
# 1) Vérifier présence et exécutable des scripts/*.sh
# 2) Vérifier mise à jour CHANGELOG.md et docs/INDEX.md
# 3) Vérifier docs/SSH_UPDATE.md si scripts/** a changé
# 4) Vérifier absence de secrets en clair dans scripts/**
# 5) Vérifier manifest_checksum si publié
- name: Tests, lint, sécurité statique
# Doit exiger un état vert
- name: Ouvrir PR de synchronisation
# Titre: "[template-sync] chore: aligner .cursor/.gitea/AGENTS.md/scripts"
# Doit inclure résumé des fichiers modifiés et la version appliquée
- name: Mettre à jour TEMPLATE_VERSION (dans PR)
# Doit remplacer la valeur par la ref appliquée

39
.gitignore vendored Normal file
View File

@ -0,0 +1,39 @@
# Dépendances
node_modules/
# Builds / artefacts
/dist/
/build/
/coverage/
/.cache/
/.turbo/
/.parcel-cache/
/tmp/
# Sorties synchronisées depuis ihm_client
/assets/ihm/
/web/ihm/
# Journaux
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
*.log
# Environnements
.env
.env.*.local
# Éditeurs / OS
.DS_Store
.vscode/
.idea/
# Jest
.jest-cache/
.jest/
!.cursor/
!AGENTS.md

263
AGENTS.md Normal file
View File

@ -0,0 +1,263 @@
# AGENTS.md
## Table des matières
- [Introduction](#introduction)
- [Principes communs](#principes-communs)
- [Agents fondamentaux](#agents-fondamentaux)
- [Agents spécialisés documentation](#agents-spécialisés-documentation)
- [Agents spécialisés tests](#agents-spécialisés-tests)
- [Agents techniques](#agents-techniques)
- [Agents frontend](#agents-frontend)
- [Agents open source et CI](#agents-open-source-et-ci)
- [Agents de synchronisation et dérogations](#agents-de-synchronisation-et-d%C3%A9rogations)
- [Matrice de coordination](#matrice-de-coordination)
- [Conclusion](#conclusion)
---
## Introduction
Ce document définit les agents, leurs rôles et leurs responsabilités dans le projet `4NK/4NK_node` et, par extension, tout dépôt dérivé de `4NK_project_template`.
Il impose une coordination stricte entre code, documentation, tests, dépendances, CI/CD, synchronisation de template et gouvernance open source.
Les règles opérationnelles détaillées sont précisées dans `.cursor/rules/` (notamment `41-ssh-automation.mdc` et `42-template-sync.mdc`).
---
## Principes communs
- Langue exclusive : français.
- Pas dexemples de code applicatif injectés dans la base.
- Toute contribution doit contenir une introduction et/ou une conclusion.
- Interdiction de secrets en clair dans le dépôt.
- Confirmation nécessaire avant `push` et `tag`.
- Toute modification impactant des éléments normatifs doit mettre à jour la documentation et le changelog.
- Le flux de publication applique un garde de release (tests/doc/build/alignement version/changelog/tag, latest vs wip).
---
## Agents fondamentaux
### Agent Fondation (Responsable)
**Missions**
- Garantir la conformité éditoriale : français, pas dexemples applicatifs, introduction/conclusion.
- Vérifier la cohérence terminologique.
**Artefacts**
- Tous fichiers.
---
### Agent Structure (Responsable)
**Missions**
- Maintenir larborescence canonique (incluant `.cursor/`, `.gitea/`, `scripts/`, `docs/SSH_UPDATE.md`).
- Archiver le contenu obsolète dans `archive/` avec métadonnées.
- Interdire toute suppression non tracée.
**Artefacts**
- `archive/`, `docs/**`, `tests/**`, `.cursor/**`, `.gitea/**`, `scripts/**`, `CHANGELOG.md`.
---
## Agents spécialisés documentation
### Agent Documentation (Responsable)
**Missions**
- Mettre à jour `docs/**` selon limpact des changements.
- Tenir `docs/INDEX.md` comme table des matières centrale.
- Produire des REX techniques dans `archive/` en cas dinvestigations multiples.
**Artefacts**
- `docs/**`, `README.md`, `archive/**`.
---
### Agent Données CSV (Responsable)
**Missions**
- Traiter les CSV comme source des modèles de données (en-têtes multi-lignes inclus).
- Exiger une définition complète de toutes les colonnes.
- Corriger et documenter les incohérences de types.
**Artefacts**
- `docs/API.md`, `docs/ARCHITECTURE.md`, `docs/USAGE.md`.
---
### Agent Documents bureautiques (Consulté)
**Missions**
- Lire `.docx` via `docx2txt`; proposer des alternatives en cas déchec.
- Documenter les imports dans `docs/INDEX.md`.
**Artefacts**
- `docs/**`, `archive/**`.
---
## Agents spécialisés tests
### Agent Tests (Responsable)
**Missions**
- Couvrir `unit`, `integration`, `connectivity`, `performance`, `external`.
- Gérer `tests/logs` et `tests/reports`.
- Exiger des tests verts avant commit.
**Artefacts**
- `tests/**`, `docs/TESTING.md`.
---
### Agent Performance (Consulté)
**Missions**
- Réaliser des benchmarks reproductibles.
- Valider limpact performance avant fusion.
**Artefacts**
- `tests/performance/`, `tests/reports/`, `docs/TESTING.md`.
---
## Agents techniques
### Agent Dépendances (Responsable)
**Missions**
- Ajouter les dépendances manquantes lorsque justifié.
- Vérifier les dernières versions stables.
- Documenter les impacts dans `ARCHITECTURE.md`, `CONFIGURATION.md`, `CHANGELOG.md`.
**Artefacts**
- `docs/ARCHITECTURE.md`, `docs/CONFIGURATION.md`, `CHANGELOG.md`.
---
### Agent Compilation (Responsable)
**Missions**
- Compiler très régulièrement et aux étapes critiques.
- Bloquer toute progression en cas derreurs de build/runtime.
**Artefacts**
- Artefacts de build, scripts doutillage.
---
### Agent Résolution (Responsable)
**Missions**
- Conduire la boucle de diagnostic complète : reproduction minimale, logs, bissection, hypothèses, tests ciblés, correctif, non-régression.
- Produire un REX quand plusieurs hypothèses ont été testées.
**Artefacts**
- `tests/**`, `archive/**`
---
### Agent SSH & scripts (Responsable)
**Missions**
- Garantir la présence et lusage correct de `scripts/auto-ssh-push.sh`, `scripts/init-ssh-env.sh`, `scripts/setup-ssh-ci.sh`.
- Assurer permissions dexécution, idempotence, journalisation non sensible, gestion derreurs robuste.
- Interdire secrets en clair, gérer via secrets CI et variables denvironnement.
- Exiger la mise à jour de `docs/SSH_UPDATE.md` à toute évolution des scripts ou des flux SSH.
**Artefacts**
- `scripts/**`, `.gitea/workflows/ci.yml`, `docs/SSH_UPDATE.md`, `docs/CONFIGURATION.md`, `CHANGELOG.md`.
---
## Agents frontend
### Agent Frontend (Responsable)
**Missions**
- Mettre en place `React.lazy`/`Suspense` (code splitting).
- Centraliser létat (Redux ou Context API).
- Abstraire les services de données.
**Artefacts**
- `docs/ARCHITECTURE.md`, `docs/TESTING.md`.
---
## Agents open source et CI
### Agent Open Source (Responsable)
**Missions**
- Maintenir : `LICENSE`, `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md`, `docs/OPEN_SOURCE_CHECKLIST.md`.
- Vérifier lalignement continu avec `4NK_node`.
**Artefacts**
- Fichiers de gouvernance cités ci-dessus.
---
### Agent Gitea (Responsable)
**Missions**
- Garantir `.gitea/ISSUE_TEMPLATE/*`, `PULL_REQUEST_TEMPLATE.md`, `.gitea/workflows/ci.yml`.
- Documenter la configuration distante dans `docs/GITEA_SETUP.md`.
- Déclencher les étapes CI pertinentes (tests, lint, sécurité, vérifs `scripts/`).
**Artefacts**
- `.gitea/**`, `docs/GITEA_SETUP.md`.
---
### Agent Versionnage (Responsable)
**Missions**
- Tenir `CHANGELOG.md` comme source unique de vérité.
- Proposer un bump sémantique justifié.
- Demander confirmation avant push et tag.
- Orchestrer le `release-guard` (CI + scripts) et consigner latest vs wip.
**Artefacts**
- `CHANGELOG.md`, `docs/RELEASE_PLAN.md`, `docs/ROADMAP.md`.
---
## Agents de synchronisation et dérogations
### Agent Synchronisation de template (Accountable)
**Références**
- `.4nk-sync.yml` (manifeste), `TEMPLATE_VERSION` (pointeur), `.gitea/workflows/template-sync.yml` (CI dédiée).
- Règles Cursor : `.cursor/rules/42-template-sync.mdc` et `.cursor/rules/10-project-structure.mdc`.
**Missions**
- Assurer lalignement automatique sur le template pour : `.cursor/**`, `.gitea/**`, `AGENTS.md`, `scripts/**`, `docs/SSH_UPDATE.md`.
- Déclencher/valider la PR `[template-sync]` créée par la CI.
- Exiger la mise à jour de `CHANGELOG.md` et `docs/INDEX.md` après synchronisation.
- Vérifier lintégrité (`manifest_checksum`, checksums de fichiers si publiés), les permissions, labsence de secrets.
- Mettre à jour `TEMPLATE_VERSION` dans la PR.
**Artefacts**
- `.4nk-sync.yml`, `TEMPLATE_VERSION`, `.cursor/**`, `.gitea/**`, `AGENTS.md`, `scripts/**`, `docs/SSH_UPDATE.md`, `CHANGELOG.md`.
---
### Agent Dérogations locales (Responsable)
**Références**
- `LOCAL_OVERRIDES.yml` (facultatif, mais recommandé).
**Missions**
- Enregistrer toute divergence locale dans le périmètre synchronisé (path, raison, propriétaire, échéance).
- Faire respecter : seules les dérogations listées et non expirées sont tolérées par la CI.
- Auditer périodiquement et résorber les dérogations.
**Artefacts**
- `LOCAL_OVERRIDES.yml`, `CHANGELOG.md` (mentionner les dérogations significatives).
---
## Matrice de coordination
| Type de changement | Agents impliqués | Artefacts principaux | Validation obligatoire |
|----------------------------------|----------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------|------------------------|
| Ajout de fonctionnalité | Documentation, Tests, Dépendances, Frontend | `docs/API.md`, `docs/USAGE.md`, `docs/ARCHITECTURE.md`, `tests/unit`, `tests/integration`, `CHANGELOG.md` (*Added*) | Oui |
| Correction de bug | Résolution, Tests, Documentation | `tests/unit`, `docs/TESTING.md`, `archive/` (REX si nécessaire), `CHANGELOG.md` (*Fixed*) | Oui |
| Refactorisation / amélioration | Structure, Documentation, Compilation | `docs/ARCHITECTURE.md`, `archive/`, `CHANGELOG.md` (*Changed*) | Oui |
| Dépendance ajoutée/mise à jour | Dépendances, Compilation, Documentation | `docs/ARCHITECTURE.md`, `docs/CONFIGURATION.md`, `CHANGELOG.md` (*Dependencies*) | Oui |
| Données CSV modifiées | Données CSV, Documentation, Tests | `docs/API.md`, `docs/ARCHITECTURE.md`, `docs/USAGE.md`, `tests/unit`, `CHANGELOG.md` (*Data model update*) | Oui |
| Migration / breaking change | Documentation, Tests, Résolution, Versionnage | `docs/MIGRATION.md`, `docs/INSTALLATION.md`, `docs/RELEASE_PLAN.md`, `docs/ROADMAP.md`, `tests/integration`, `CHANGELOG.md` (*Breaking*) | Oui |
| Sécurité / audit | Documentation, Tests, Open Source, Sécurité proactive | `docs/SECURITY_AUDIT.md`, `tests/external`, `tests/connectivity`, `CHANGELOG.md` (*Security*) | Oui |
| Préparation open source / CI | Open Source, Gitea, Versionnage, Documentation communautaire, Contributeurs externes | `.gitea/**`, `docs/GITEA_SETUP.md`, `docs/OPEN_SOURCE_CHECKLIST.md`, `CHANGELOG.md` (*CI/CD* / *Governance*) | Oui |
| Optimisation performance | Performance, Tests, Documentation | `tests/performance`, `tests/reports`, `docs/ARCHITECTURE.md`, `CHANGELOG.md` (*Performance*) | Oui |
| Évolution frontend | Frontend, Documentation, Tests | `docs/ARCHITECTURE.md`, `docs/USAGE.md`, `tests/integration`, `CHANGELOG.md` (*Frontend*) | Oui |
| Évolution CI/CD ou scripts SSH | SSH & scripts, Gitea, Versionnage, Documentation | `scripts/**`, `.gitea/workflows/ci.yml`, `docs/SSH_UPDATE.md`, `docs/CONFIGURATION.md`, `CHANGELOG.md` (*CI/CD*) | Oui |
| **Synchronisation de template** | **Synchronisation de template**, Gitea, Versionnage, Structure, Documentation, SSH & scripts | `.4nk-sync.yml`, `TEMPLATE_VERSION`, `.cursor/**`, `.gitea/**`, `AGENTS.md`, `scripts/**`, `docs/SSH_UPDATE.md`, `CHANGELOG.md` | **Oui** |
| Dérogation locale contrôlée | Dérogations locales, Gitea, Synchronisation de template, Versionnage | `LOCAL_OVERRIDES.yml`, `CHANGELOG.md` (mention), CI tolérante uniquement sur chemins listés et non expirés | Oui |
---
## Conclusion
Ce `AGENTS.md` mis à jour introduit l**Agent Synchronisation de template** et l**Agent Dérogations locales**, renforce l**Agent SSH & scripts**, et rattache lensemble aux règles Cursor et à la CI Gitea. La matrice de coordination formalise les validations obligatoires pour chaque type de changement, garantissant cohérence structurelle, qualité documentaire, sécurité, traçabilité et stabilité à long terme sur tous les projets issus de `4NK_project_template`.

14
CHANGELOG.md Normal file
View File

@ -0,0 +1,14 @@
# Changelog - sdk_wallet
Ce projet suit le format Keep a Changelog et le Semantic Versioning.
## [0.1.0] - 2025-08-27
### Added
- Squelette React Native (Redux, React.lazy/Suspense)
- Pont WebView ↔ ihm_client via postMessage
- Scripts PowerShell: build-ihm, copy-ihm, sync:ihm
- TypeScript, Jest (jsdom), config TS/Jest
- Écran `WalletScreen` et composant `WebWallet`
### Notes
- Chargement local des assets d`ihm_client` prévu via `assets/ihm/`

10
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,10 @@
# Code de Conduite
Nous nous engageons à fournir un environnement ouvert et accueillant. Soyez respectueux, constructif et professionnel. Tout comportement abusif, discriminatoire ou harcelant nest pas toléré.
- Agissez de bonne foi, assumez des intentions positives
- Pas de harcèlement, dintimidation ou de propos discriminatoires
- Respectez la confidentialité et la sécurité
- Utilisez des canaux appropriés pour signaler des incidents
Contact: contact@4nkweb.com

23
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,23 @@
# Contribuer à sdk_wallet
Merci de votre intérêt !
## Démarrage
- Node 18+ / 20+, pnpm ou npm
- `npm install`
- `npm run typecheck && npm test`
## Branches et commits
- Feature branches: `feat/<scope>`
- Fixes: `fix/<scope>`
- Commits conventionnels (conventional commits)
## Tests
- Ajoutez des tests dans `tests/`
- Isolez létat et les ressources, pas deffets globaux
## Documentation
- Mettez à jour `docs/` et `CHANGELOG.md` pour chaque changement utilisateur
## Licence
- MIT

22
LICENSE Normal file
View File

@ -0,0 +1,22 @@
MIT License
Copyright (c) 2025 4NK
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

27
docs/ARCHITECTURE.md Normal file
View File

@ -0,0 +1,27 @@
# Architecture - sdk_wallet
## Vue densemble
- Application mobile React Native
- État centralisé avec Redux Toolkit
- UI Web intégrée via `react-native-webview` qui charge `ihm_client` (build Vite)
- Pont de messages `window.postMessage` redirigé vers `ReactNativeWebView.postMessage`
## Flux
1. Lapp charge `assets/ihm/index.html` (build de `ihm_client`)
2. Le script injecté remappe `window.postMessage` et expose `window.__RN_RECEIVE__`
3. `ihm_client` émet `LISTENING`, `LINK_ACCEPTED`, etc. → captés côté RN
4. RN met à jour Redux (tokens, état), puis peut envoyer des messages: `REQUEST_LINK`, `VALIDATE_TOKEN`, etc.
## Découpage
- `src/bridge/` : sérialisation et gestion des messages
- `src/components/` : `WebWallet` (WebView)
- `src/screens/` : `WalletScreen`
- `src/store/` : état (tokens, dernier message)
## Sécurité
- Respect de lorigine dans `ihm_client` (réponses vers `event.origin`)
- Les tokens ne sortent pas du store sans action explicite
## Performances
- Code splitting avec `React.lazy`/`Suspense`
- Build `ihm_client` optimisé via Vite

6
docs/INDEX.md Normal file
View File

@ -0,0 +1,6 @@
# Documentation - sdk_wallet
- Architecture: `ARCHITECTURE.md`
- Intégration iframe/WebView: `INTEGRATION.md`
- Tests: `TESTING.md`
- Notes de version: `../CHANGELOG.md`

20
docs/INTEGRATION.md Normal file
View File

@ -0,0 +1,20 @@
# Intégration WebView ↔ ihm_client
## Principes
- `ihm_client` parle via `window.postMessage` (cf. `ihm_client/docs/INTEGRATION_IFRAME.md`)
- En mobile, on charge `ihm_client` dans une WebView
- On redirige `window.postMessage` vers `ReactNativeWebView.postMessage`
- Canal entrant: RN appelle `window.__RN_RECEIVE__(jsonString)` pour simuler un `MessageEvent`
## Messages pris en charge (extraits)
- REQUEST_LINK → LINK_ACCEPTED|ERROR
- VALIDATE_TOKEN → VALIDATE_TOKEN
- RENEW_TOKEN → RENEW_TOKEN
## Mapping côté RN
- Sortant: RN → `__RN_RECEIVE__(jsonString)` (déclenche un `message` côté page)
- Entrant: page → `postMessage(any)` redirigé vers RN `onMessage`
## Sécurité
- `ihm_client` valide lorigine et les tokens
- RN ne manipule pas directement les tokens côté page

12
docs/TESTING.md Normal file
View File

@ -0,0 +1,12 @@
# Tests - sdk_wallet
## Portée
- Bridge: sérialisation, réception des messages, mise à jour Redux
- Store: reducers `setTokens`, `setLastMessageType`
## Commandes
- `npm test` (Jest + ts-jest, jsdom)
## Isolation
- Pas daccès réseau
- Pas dexemples exécutables; tests en mémoire

File diff suppressed because one or more lines are too long

BIN
ihm/assets/4nk_image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

BIN
ihm/assets/4nk_revoke.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

BIN
ihm/assets/bgd.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 509 KiB

BIN
ihm/assets/camera.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

34
ihm/assets/home.js Normal file
View File

@ -0,0 +1,34 @@
document.querySelectorAll('.tab').forEach(tab => {
tab.addEventListener('click', () => {
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
tab.classList.add('active');
document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
document.getElementById(tab.getAttribute('data-tab')).classList.add('active');
});
});
function toggleMenu() {
var menu = document.getElementById('menu');
if (menu.style.display === 'block') {
menu.style.display = 'none';
} else {
menu.style.display = 'block';
}
}
//// Modal
function openModal() {
document.getElementById('modal').style.display = 'flex';
}
function closeModal() {
document.getElementById('modal').style.display = 'none';
}
// Close modal when clicking outside of it
window.onclick = function(event) {
const modal = document.getElementById('modal');
if (event.target === modal) {
closeModal();
}
}

BIN
ihm/assets/qr_code.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

BIN
ihm/bgd-I4_In7H5.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 509 KiB

877
ihm/index--72nVAQK.css Normal file
View File

@ -0,0 +1,877 @@
:root {
--primary-color
: #3A506B;
/* Bleu métallique */
--secondary-color
: #B0BEC5;
/* Gris acier */
--accent-color
: #D68C45;
/* Cuivre */
}
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #f4f4f4;
background-image: url(/bgd-I4_In7H5.webp);
background-repeat:no-repeat;
background-size: cover;
background-blend-mode :soft-light;
height: 100vh;
}
.message {
margin: 30px 0;
font-size: 14px;
overflow-wrap: anywhere;
}
.message strong{
font-family: "Segoe UI Emoji", "Noto Color Emoji", "Apple Color Emoji", sans-serif;
font-size: 20px;
}
/** Modal Css */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
justify-content: center;
align-items: center;
z-index: 3;
}
.modal-content {
width: 55%;
height: 30%;
background-color: white;
border-radius: 4px;
padding: 20px;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
}
.modal-title {
margin: 0;
padding-bottom: 8px;
width: 100%;
font-size: 0.9em;
border-bottom: 1px solid #ccc;
}
.confirmation-box {
/* margin-top: 20px; */
align-content: center;
width: 70%;
height: 20%;
/* padding: 20px; */
font-size: 1.5em;
color: #333333;
top: 5%;
position: relative;
}
/* Confirmation Modal Styles */
#confirmation-modal {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 1000;
}
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
}
.modal-content {
background: white;
padding: 20px;
border-radius: 8px;
width: 90%;
max-width: 500px;
max-height: 80vh;
overflow-y: auto;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.modal-confirmation {
text-align: left;
padding: 10px;
}
.modal-confirmation h3 {
margin-bottom: 15px;
color: var(--primary-color);
font-size: 1.1em;
}
.modal-confirmation p {
margin: 8px 0;
font-size: 0.9em;
line-height: 1.4;
}
.modal-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-top: 20px;
padding-top: 15px;
border-top: 1px solid #eee;
}
.modal-footer button {
padding: 8px 16px;
border-radius: 4px;
border: none;
cursor: pointer;
font-size: 0.9em;
}
.btn-primary {
background: var(--primary-color);
color: white;
}
.btn-secondary {
background: var(--secondary-color);
color: white;
}
/* Responsive adjustments */
@media only screen and (max-width: 600px) {
.modal-content {
width: 95%;
margin: 10px;
padding: 15px;
}
.modal-confirmation h3 {
font-size: 1em;
}
.modal-confirmation p {
font-size: 0.85em;
}
}
.nav-wrapper {
position: fixed;
z-index: 2;
background: radial-gradient(circle, white, var(--primary-color));
/* background-color: #CFD8DC; */
display: flex;
justify-content: flex-end;
align-items: center;
color: #37474F;
height: 9vh;
width: 100vw;
left: 0;
top: 0;
box-shadow: 0px 8px 10px -5px rgba(0, 0, 0, .2), 0px 16px 24px 2px rgba(0, 0, 0, .14), 0px 6px 30px 5px rgba(0, 0, 0, .12);
.nav-right-icons {
display: flex;
.notification-container {
position: relative;
display: inline-block;
}
.notification-bell, .burger-menu {
z-index: 3;
height: 20px;
width: 20px;
margin-right: 1rem;
}
.notification-badge {
position: absolute;
top: -.7rem;
left: -.8rem;
background-color: red;
color: white;
border-radius: 50%;
padding: 2.5px 6px;
font-size: 0.8em;
font-weight: bold;
}
}
.notification-board {
position: absolute;
width: 20rem;
min-height: 8rem;
background-color: white;
right: 0.5rem;
display: none;
border-radius: 4px;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
display: none;
.notification-element {
padding: .8rem 0;
width: 100%;
&:hover {
background-color: rgba(26, 28, 24, .08);
}
}
.notification-element:not(:last-child) {
border-bottom: 1px solid;
}
}
}
.brand-logo {
height: 100%;
width: 100vw;
align-content: center;
position: relative;
display: flex;
position: absolute;
align-items: center;
justify-content: center;
text-align: center;
font-size: 1.5em;
font-weight: bold;
}
.container {
text-align: center;
display: grid;
height: 100vh;
grid-template-columns: repeat(7, 1fr);
gap: 10px;
grid-auto-rows: 10vh 15vh 1fr;
}
.title-container {
grid-column: 2 / 7;
grid-row: 2;
}
.page-container {
grid-column: 2 / 7;
grid-row: 3 ;
justify-content: center;
display: flex;
padding: 1rem;
box-sizing: border-box;
max-height: 60vh;
}
h1 {
font-size: 2em;
margin: 20px 0;
}
@media only screen and (min-width: 600px) {
.tab-container {
display: none;
}
.page-container {
display: flex;
align-items: center;
}
.process-container {
grid-column: 3 / 6;
grid-row: 3 ;
.card {
min-width: 40vw;
}
}
.separator {
width: 2px;
background-color: #78909C;
height: 80%;
margin: 0 0.5em;
}
.tab-content {
display: flex;
flex-direction: column;
justify-content: space-evenly;
align-items: center;
height: 80%;
}
}
@media only screen and (max-width: 600px) {
.process-container {
grid-column: 2 / 7;
grid-row: 3 ;
}
.container {
grid-auto-rows: 10vh 15vh 15vh 1fr;
}
.tab-container {
grid-column: 1 / 8;
grid-row: 3;
}
.page-container {
grid-column: 2 / 7;
grid-row: 4 ;
}
.separator {
display: none;
}
.tabs {
display: flex;
flex-grow: 1;
overflow: hidden;
z-index: 1;
border-bottom-style: solid;
border-bottom-width: 1px;
border-bottom-color: #E0E4D6;
}
.tab {
flex: 1;
text-align: center;
padding: 10px 0;
cursor: pointer;
font-size: 1em;
color: #6200ea;
&:hover {
background-color: rgba(26, 28, 24, .08);
}
}
.tab.active {
border-bottom: 2px solid #6200ea;
font-weight: bold;
}
.card.tab-content {
display: none;
}
.tab-content.active {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 80%;
}
.modal-content {
width: 80%;
height: 20%;
}
}
.qr-code {
display: flex;
justify-content: center;
align-items: center;
height: 200px;
}
.emoji-display {
font-family: "Segoe UI Emoji", "Noto Color Emoji", "Apple Color Emoji", sans-serif;
font-size: 20px;
}
#emoji-display-2{
margin-top: 30px;
font-family: "Segoe UI Emoji", "Noto Color Emoji", "Apple Color Emoji", sans-serif;
font-size: 20px;
}
#okButton{
margin-bottom: 2em;
cursor: pointer;
background-color: #D0D0D7;
color: white;
border-style: none;
border-radius: 5px;
color: #000;
padding: 2px;
margin-top: 10px;
}
.pairing-request {
font-family: "Segoe UI Emoji", "Noto Color Emoji", "Apple Color Emoji", sans-serif;
font-size: 14px;
margin-top: 0px;
}
.sp-address-btn {
margin-bottom: 2em;
cursor: pointer;
background-color: #D0D0D7;
color: white;
border-style: none;
border-radius: 5px;
color: #000;
padding: 2px;
}
.camera-card {
display: flex;
justify-content: center;
align-items: center;
/* height: 200px; */
}
.btn {
display: inline-block;
padding: 10px 20px;
background-color: var(--primary-color);
color: white;
text-align: center;
border-radius: 5px;
cursor: pointer;
text-decoration: none;
}
.btn:hover {
background-color: #3700b3;
}
.card {
min-width: 300px;
border: 1px solid #e0e0e0;
border-radius: 8px;
background-color: white;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
box-sizing: border-box;
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
height: 60vh;
justify-content: flex-start;
padding: 1rem;
overflow-y: auto;
}
.card-content {
flex-grow: 1;
flex-direction: column;
display: flex;
justify-content: flex-start;
align-items: center;
text-align: left;
font-size: .8em;
position: relative;
left: 2vw;
width: 90%;
.process-title {
font-weight: bold;
padding: 1rem 0;
}
.process-element {
padding: .4rem 0;
&:hover {
background-color: rgba(26, 28, 24, .08);
}
&.selected {
background-color: rgba(26, 28, 24, .08);
}
}
}
.card-description {
padding: 20px;
font-size: 1em;
color: #333;
width: 90%;
height: 50px;
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 0px;
}
.card-action {
width: 100%;
}
.menu-content {
display: none;
position: absolute;
top: 3.4rem;
right: 1rem;
background-color: white;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
border-radius: 5px;
overflow: hidden;
}
.menu-content a {
display: block;
padding: 10px 20px;
text-decoration: none;
color: #333;
border-bottom: 1px solid #e0e0e0;
&:hover {
background-color: rgba(26, 28, 24, .08);
}
}
.menu-content a:last-child {
border-bottom: none;
}
.qr-code-scanner {
display: none;
}
/* QR READER */
#qr-reader div {
position: inherit;
}
#qr-reader div img{
top: 15px ;
right: 25px;
margin-top: 5px;
}
/* INPUT CSS **/
.input-container {
position: relative;
width: 100%;
background-color: #ECEFF1;
}
.input-field {
width: 36vw;
padding: 10px 0;
font-size: 1em;
border: none;
border-bottom: 1px solid #ccc;
outline: none;
background: transparent;
transition: border-color 0.3s;
}
.input-field:focus {
border-bottom: 2px solid #6200ea;
}
.input-label {
position: absolute;
margin-top: -0.5em;
top: 0;
left: 0;
padding: 10px 0;
font-size: 1em;
color: #999;
pointer-events: none;
transition: transform 0.3s, color 0.3s, font-size 0.3s;
}
.input-field:focus + .input-label,
.input-field:not(:placeholder-shown) + .input-label {
transform: translateY(-20px);
font-size: 0.8em;
color: #6200ea;
}
.input-underline {
position: absolute;
bottom: 0;
left: 50%;
width: 0;
height: 2px;
background-color: #6200ea;
transition: width 0.3s, left 0.3s;
}
.input-field:focus ~ .input-underline {
width: 100%;
left: 0;
}
.dropdown-content {
position: absolute;
flex-direction: column;
top: 100%;
left: 0;
width: 100%;
max-height: 150px;
overflow-y: auto;
border: 1px solid #ccc;
border-radius: 4px;
background-color: white;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
display: none;
z-index: 1;
}
.dropdown-content span {
padding: 10px;
cursor: pointer;
list-style: none;
}
.dropdown-content span:hover {
background-color: #f0f0f0;
}
/** AUTOCOMPLETE **/
select[data-multi-select-plugin] {
display: none !important;
}
.multi-select-component {
width: 36vw;
padding: 5px 0;
font-size: 1em;
border: none;
border-bottom: 1px solid #ccc;
outline: none;
background: transparent;
display: flex;
flex-direction: row;
height: auto;
width: 100%;
-o-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
}
.autocomplete-list {
border-radius: 4px 0px 0px 4px;
}
.multi-select-component:focus-within {
box-shadow: inset 0px 0px 0px 2px #78ABFE;
}
.multi-select-component .btn-group {
display: none !important;
}
.multiselect-native-select .multiselect-container {
width: 100%;
}
.selected-processes {
background-color: white;
padding: 0.4em;
}
.selected-wrapper {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
display: inline-block;
border: 1px solid #d9d9d9;
background-color: #ededed;
white-space: nowrap;
margin: 1px 5px 5px 0;
height: 22px;
vertical-align: top;
cursor: default;
}
.selected-wrapper .selected-label {
max-width: 514px;
display: inline-block;
overflow: hidden;
text-overflow: ellipsis;
padding-left: 4px;
vertical-align: top;
}
.selected-wrapper .selected-close {
display: inline-block;
text-decoration: none;
font-size: 14px;
line-height: 1.49em;
margin-left: 5px;
padding-bottom: 10px;
height: 100%;
vertical-align: top;
padding-right: 4px;
opacity: 0.2;
color: #000;
text-shadow: 0 1px 0 #fff;
font-weight: 700;
}
.search-container {
display: flex;
flex-direction: row;
}
.search-container .selected-input {
background: none;
border: 0;
height: 20px;
width: 60px;
padding: 0;
margin-bottom: 6px;
-webkit-box-shadow: none;
box-shadow: none;
}
.search-container .selected-input:focus {
outline: none;
}
.dropdown-icon.active {
transform: rotateX(180deg)
}
.search-container .dropdown-icon {
display: inline-block;
padding: 10px 5px;
position: absolute;
top: 5px;
right: 5px;
width: 10px;
height: 10px;
border: 0 !important;
/* needed */
-webkit-appearance: none;
-moz-appearance: none;
/* SVG background image */
background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2212%22%20height%3D%2212%22%20viewBox%3D%220%200%2012%2012%22%3E%3Ctitle%3Edown-arrow%3C%2Ftitle%3E%3Cg%20fill%3D%22%23818181%22%3E%3Cpath%20d%3D%22M10.293%2C3.293%2C6%2C7.586%2C1.707%2C3.293A1%2C1%2C0%2C0%2C0%2C.293%2C4.707l5%2C5a1%2C1%2C0%2C0%2C0%2C1.414%2C0l5-5a1%2C1%2C0%2C1%2C0-1.414-1.414Z%22%20fill%3D%22%23818181%22%3E%3C%2Fpath%3E%3C%2Fg%3E%3C%2Fsvg%3E");
background-position: center;
background-size: 10px;
background-repeat: no-repeat;
}
.search-container ul {
position: absolute;
list-style: none;
padding: 0;
z-index: 3;
margin-top: 29px;
width: 100%;
right: 0px;
background: #fff;
border: 1px solid #ccc;
border-top: none;
border-bottom: none;
-webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
}
.search-container ul :focus {
outline: none;
}
.search-container ul li {
display: block;
text-align: left;
padding: 8px 29px 2px 12px;
border-bottom: 1px solid #ccc;
font-size: 14px;
min-height: 31px;
}
.search-container ul li:first-child {
border-top: 1px solid #ccc;
border-radius: 4px 0px 0 0;
}
.search-container ul li:last-child {
border-radius: 4px 0px 0 0;
}
.search-container ul li:hover.not-cursor {
cursor: default;
}
.search-container ul li:hover {
color: #333;
background-color: #f0f0f0;
;
border-color: #adadad;
cursor: pointer;
}
/* Adding scrool to select options */
.autocomplete-list {
max-height: 130px;
overflow-y: auto;
}
/**************************************** Process page card ******************************************************/
.process-card {
min-width: 300px;
border: 1px solid #e0e0e0;
border-radius: 8px;
background-color: white;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
min-height: 40vh;
max-height: 60vh;
justify-content: space-between;
padding: 1rem;
overflow-y: auto;
}
.process-card-content {
text-align: left;
font-size: .8em;
position: relative;
left: 2vw;
width: 90%;
.process-title {
font-weight: bold;
padding: 1rem 0;
}
.process-element {
padding: .4rem 0;
&:hover {
background-color: rgba(26, 28, 24, .08);
}
&.selected {
background-color: rgba(26, 28, 24, .08);
}
}
.selected-process-zone {
background-color: rgba(26, 28, 24, .08);
}
}
.process-card-description {
padding: 20px;
font-size: 1em;
color: #333;
width: 90%;
}
.process-card-action {
width: 100%;
}

11827
ihm/index-CZvdFchg.mjs Normal file

File diff suppressed because one or more lines are too long

1
ihm/index.html Normal file
View File

@ -0,0 +1 @@
<!doctype html><html lang="en"><head><meta charset="UTF-8"><meta name="author" content="4NK"><meta name="description" content="4NK Web5 Platform"><meta name="keywords" content="4NK web5 bitcoin blockchain decentralize dapps relay contract"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="stylesheet" href="/style/4nk.css"><script src="https://unpkg.com/html5-qrcode"></script><title>4NK Application</title><script type="module" crossorigin src="/index-CZvdFchg.mjs"></script><link rel="stylesheet" crossorigin href="/index--72nVAQK.css"></head><body><div id="header-container"></div><div id="containerId" class="container"></div></body></html>

View File

@ -0,0 +1,100 @@
const createWorker=()=>new Worker(URL.createObjectURL(new Blob([`class x{constructor(a,b){this.width=b;this.height=a.length/b;this.data=a}static createEmpty(a,b){return new x(new Uint8ClampedArray(a*b),a)}get(a,b){return 0>a||a>=this.width||0>b||b>=this.height?!1:!!this.data[b*this.width+a]}set(a,b,c){this.data[b*this.width+a]=c?1:0}setRegion(a,b,c,d,e){for(let f=b;f<b+d;f++)for(let g=a;g<a+c;g++)this.set(g,f,!!e)}}
class A{constructor(a,b,c){this.width=a;a*=b;if(c&&c.length!==a)throw Error("Wrong buffer size");this.data=c||new Uint8ClampedArray(a)}get(a,b){return this.data[b*this.width+a]}set(a,b,c){this.data[b*this.width+a]=c}}
class ba{constructor(a){this.bitOffset=this.byteOffset=0;this.bytes=a}readBits(a){if(1>a||32<a||a>this.available())throw Error("Cannot read "+a.toString()+" bits");var b=0;if(0<this.bitOffset){b=8-this.bitOffset;var c=a<b?a:b;b-=c;b=(this.bytes[this.byteOffset]&255>>8-c<<b)>>b;a-=c;this.bitOffset+=c;8===this.bitOffset&&(this.bitOffset=0,this.byteOffset++)}if(0<a){for(;8<=a;)b=b<<8|this.bytes[this.byteOffset]&255,this.byteOffset++,a-=8;0<a&&(c=8-a,b=b<<a|(this.bytes[this.byteOffset]&255>>c<<c)>>c,
this.bitOffset+=a)}return b}available(){return 8*(this.bytes.length-this.byteOffset)-this.bitOffset}}var B,C=B||(B={});C.Numeric="numeric";C.Alphanumeric="alphanumeric";C.Byte="byte";C.Kanji="kanji";C.ECI="eci";C.StructuredAppend="structuredappend";var D,E=D||(D={});E[E.Terminator=0]="Terminator";E[E.Numeric=1]="Numeric";E[E.Alphanumeric=2]="Alphanumeric";E[E.Byte=4]="Byte";E[E.Kanji=8]="Kanji";E[E.ECI=7]="ECI";E[E.StructuredAppend=3]="StructuredAppend";let F="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:".split("");
function ca(a,b){let c=[],d="";b=a.readBits([8,16,16][b]);for(let e=0;e<b;e++){let f=a.readBits(8);c.push(f)}try{d+=decodeURIComponent(c.map(e=>\`%\${("0"+e.toString(16)).substr(-2)}\`).join(""))}catch(e){}return{bytes:c,text:d}}
function da(a,b){a=new ba(a);let c=9>=b?0:26>=b?1:2;for(b={text:"",bytes:[],chunks:[],version:b};4<=a.available();){var d=a.readBits(4);if(d===D.Terminator)return b;if(d===D.ECI)0===a.readBits(1)?b.chunks.push({type:B.ECI,assignmentNumber:a.readBits(7)}):0===a.readBits(1)?b.chunks.push({type:B.ECI,assignmentNumber:a.readBits(14)}):0===a.readBits(1)?b.chunks.push({type:B.ECI,assignmentNumber:a.readBits(21)}):b.chunks.push({type:B.ECI,assignmentNumber:-1});else if(d===D.Numeric){var e=a,f=[];d="";for(var g=
e.readBits([10,12,14][c]);3<=g;){var h=e.readBits(10);if(1E3<=h)throw Error("Invalid numeric value above 999");var k=Math.floor(h/100),m=Math.floor(h/10)%10;h%=10;f.push(48+k,48+m,48+h);d+=k.toString()+m.toString()+h.toString();g-=3}if(2===g){g=e.readBits(7);if(100<=g)throw Error("Invalid numeric value above 99");e=Math.floor(g/10);g%=10;f.push(48+e,48+g);d+=e.toString()+g.toString()}else if(1===g){e=e.readBits(4);if(10<=e)throw Error("Invalid numeric value above 9");f.push(48+e);d+=e.toString()}b.text+=
d;b.bytes.push(...f);b.chunks.push({type:B.Numeric,text:d})}else if(d===D.Alphanumeric){e=a;f=[];d="";for(g=e.readBits([9,11,13][c]);2<=g;)m=e.readBits(11),k=Math.floor(m/45),m%=45,f.push(F[k].charCodeAt(0),F[m].charCodeAt(0)),d+=F[k]+F[m],g-=2;1===g&&(e=e.readBits(6),f.push(F[e].charCodeAt(0)),d+=F[e]);b.text+=d;b.bytes.push(...f);b.chunks.push({type:B.Alphanumeric,text:d})}else if(d===D.Byte)d=ca(a,c),b.text+=d.text,b.bytes.push(...d.bytes),b.chunks.push({type:B.Byte,bytes:d.bytes,text:d.text});
else if(d===D.Kanji){f=a;d=[];e=f.readBits([8,10,12][c]);for(g=0;g<e;g++)k=f.readBits(13),k=Math.floor(k/192)<<8|k%192,k=7936>k?k+33088:k+49472,d.push(k>>8,k&255);f=(new TextDecoder("shift-jis")).decode(Uint8Array.from(d));b.text+=f;b.bytes.push(...d);b.chunks.push({type:B.Kanji,bytes:d,text:f})}else d===D.StructuredAppend&&b.chunks.push({type:B.StructuredAppend,currentSequence:a.readBits(4),totalSequence:a.readBits(4),parity:a.readBits(8)})}if(0===a.available()||0===a.readBits(a.available()))return b}
class G{constructor(a,b){if(0===b.length)throw Error("No coefficients.");this.field=a;let c=b.length;if(1<c&&0===b[0]){let d=1;for(;d<c&&0===b[d];)d++;if(d===c)this.coefficients=a.zero.coefficients;else for(this.coefficients=new Uint8ClampedArray(c-d),a=0;a<this.coefficients.length;a++)this.coefficients[a]=b[d+a]}else this.coefficients=b}degree(){return this.coefficients.length-1}isZero(){return 0===this.coefficients[0]}getCoefficient(a){return this.coefficients[this.coefficients.length-1-a]}addOrSubtract(a){if(this.isZero())return a;
if(a.isZero())return this;let b=this.coefficients;a=a.coefficients;b.length>a.length&&([b,a]=[a,b]);let c=new Uint8ClampedArray(a.length),d=a.length-b.length;for(var e=0;e<d;e++)c[e]=a[e];for(e=d;e<a.length;e++)c[e]=b[e-d]^a[e];return new G(this.field,c)}multiply(a){if(0===a)return this.field.zero;if(1===a)return this;let b=this.coefficients.length,c=new Uint8ClampedArray(b);for(let d=0;d<b;d++)c[d]=this.field.multiply(this.coefficients[d],a);return new G(this.field,c)}multiplyPoly(a){if(this.isZero()||
a.isZero())return this.field.zero;let b=this.coefficients,c=b.length;a=a.coefficients;let d=a.length,e=new Uint8ClampedArray(c+d-1);for(let f=0;f<c;f++){let g=b[f];for(let h=0;h<d;h++)e[f+h]=H(e[f+h],this.field.multiply(g,a[h]))}return new G(this.field,e)}multiplyByMonomial(a,b){if(0>a)throw Error("Invalid degree less than 0");if(0===b)return this.field.zero;let c=this.coefficients.length;a=new Uint8ClampedArray(c+a);for(let d=0;d<c;d++)a[d]=this.field.multiply(this.coefficients[d],b);return new G(this.field,
a)}evaluateAt(a){let b=0;if(0===a)return this.getCoefficient(0);let c=this.coefficients.length;if(1===a)return this.coefficients.forEach(d=>{b^=d}),b;b=this.coefficients[0];for(let d=1;d<c;d++)b=H(this.field.multiply(a,b),this.coefficients[d]);return b}}function H(a,b){return a^b}
class ea{constructor(a,b,c){this.primitive=a;this.size=b;this.generatorBase=c;this.expTable=Array(this.size);this.logTable=Array(this.size);a=1;for(b=0;b<this.size;b++)this.expTable[b]=a,a*=2,a>=this.size&&(a=(a^this.primitive)&this.size-1);for(a=0;a<this.size-1;a++)this.logTable[this.expTable[a]]=a;this.zero=new G(this,Uint8ClampedArray.from([0]));this.one=new G(this,Uint8ClampedArray.from([1]))}multiply(a,b){return 0===a||0===b?0:this.expTable[(this.logTable[a]+this.logTable[b])%(this.size-1)]}inverse(a){if(0===
a)throw Error("Can't invert 0");return this.expTable[this.size-this.logTable[a]-1]}buildMonomial(a,b){if(0>a)throw Error("Invalid monomial degree less than 0");if(0===b)return this.zero;a=new Uint8ClampedArray(a+1);a[0]=b;return new G(this,a)}log(a){if(0===a)throw Error("Can't take log(0)");return this.logTable[a]}exp(a){return this.expTable[a]}}
function fa(a,b,c,d){b.degree()<c.degree()&&([b,c]=[c,b]);let e=a.zero;for(var f=a.one;c.degree()>=d/2;){var g=b;let h=e;b=c;e=f;if(b.isZero())return null;c=g;f=a.zero;g=b.getCoefficient(b.degree());for(g=a.inverse(g);c.degree()>=b.degree()&&!c.isZero();){let k=c.degree()-b.degree(),m=a.multiply(c.getCoefficient(c.degree()),g);f=f.addOrSubtract(a.buildMonomial(k,m));c=c.addOrSubtract(b.multiplyByMonomial(k,m))}f=f.multiplyPoly(e).addOrSubtract(h);if(c.degree()>=b.degree())return null}d=f.getCoefficient(0);
if(0===d)return null;a=a.inverse(d);return[f.multiply(a),c.multiply(a)]}
function ha(a,b){let c=new Uint8ClampedArray(a.length);c.set(a);a=new ea(285,256,0);var d=new G(a,c),e=new Uint8ClampedArray(b),f=!1;for(var g=0;g<b;g++){var h=d.evaluateAt(a.exp(g+a.generatorBase));e[e.length-1-g]=h;0!==h&&(f=!0)}if(!f)return c;d=new G(a,e);d=fa(a,a.buildMonomial(b,1),d,b);if(null===d)return null;b=d[0];g=b.degree();if(1===g)b=[b.getCoefficient(1)];else{e=Array(g);f=0;for(h=1;h<a.size&&f<g;h++)0===b.evaluateAt(h)&&(e[f]=a.inverse(h),f++);b=f!==g?null:e}if(null==b)return null;e=d[1];
f=b.length;d=Array(f);for(g=0;g<f;g++){h=a.inverse(b[g]);let k=1;for(let m=0;m<f;m++)g!==m&&(k=a.multiply(k,H(1,a.multiply(b[m],h))));d[g]=a.multiply(e.evaluateAt(h),a.inverse(k));0!==a.generatorBase&&(d[g]=a.multiply(d[g],h))}for(e=0;e<b.length;e++){f=c.length-1-a.log(b[e]);if(0>f)return null;c[f]^=d[e]}return c}
let I=[{infoBits:null,versionNumber:1,alignmentPatternCenters:[],errorCorrectionLevels:[{ecCodewordsPerBlock:7,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:19}]},{ecCodewordsPerBlock:10,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:16}]},{ecCodewordsPerBlock:13,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:13}]},{ecCodewordsPerBlock:17,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:9}]}]},{infoBits:null,versionNumber:2,alignmentPatternCenters:[6,18],errorCorrectionLevels:[{ecCodewordsPerBlock:10,ecBlocks:[{numBlocks:1,
dataCodewordsPerBlock:34}]},{ecCodewordsPerBlock:16,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:28}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:22}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:16}]}]},{infoBits:null,versionNumber:3,alignmentPatternCenters:[6,22],errorCorrectionLevels:[{ecCodewordsPerBlock:15,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:55}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:44}]},{ecCodewordsPerBlock:18,
ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:17}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:13}]}]},{infoBits:null,versionNumber:4,alignmentPatternCenters:[6,26],errorCorrectionLevels:[{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:80}]},{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:32}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:24}]},{ecCodewordsPerBlock:16,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:9}]}]},
{infoBits:null,versionNumber:5,alignmentPatternCenters:[6,30],errorCorrectionLevels:[{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:108}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:43}]},{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:15},{numBlocks:2,dataCodewordsPerBlock:16}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:11},{numBlocks:2,dataCodewordsPerBlock:12}]}]},{infoBits:null,versionNumber:6,alignmentPatternCenters:[6,
34],errorCorrectionLevels:[{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:68}]},{ecCodewordsPerBlock:16,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:27}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:19}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:15}]}]},{infoBits:31892,versionNumber:7,alignmentPatternCenters:[6,22,38],errorCorrectionLevels:[{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:78}]},{ecCodewordsPerBlock:18,
ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:31}]},{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:14},{numBlocks:4,dataCodewordsPerBlock:15}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:13},{numBlocks:1,dataCodewordsPerBlock:14}]}]},{infoBits:34236,versionNumber:8,alignmentPatternCenters:[6,24,42],errorCorrectionLevels:[{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:97}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:38},
{numBlocks:2,dataCodewordsPerBlock:39}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:18},{numBlocks:2,dataCodewordsPerBlock:19}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:14},{numBlocks:2,dataCodewordsPerBlock:15}]}]},{infoBits:39577,versionNumber:9,alignmentPatternCenters:[6,26,46],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:36},
{numBlocks:2,dataCodewordsPerBlock:37}]},{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:16},{numBlocks:4,dataCodewordsPerBlock:17}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:12},{numBlocks:4,dataCodewordsPerBlock:13}]}]},{infoBits:42195,versionNumber:10,alignmentPatternCenters:[6,28,50],errorCorrectionLevels:[{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:68},{numBlocks:2,dataCodewordsPerBlock:69}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,
dataCodewordsPerBlock:43},{numBlocks:1,dataCodewordsPerBlock:44}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:19},{numBlocks:2,dataCodewordsPerBlock:20}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:15},{numBlocks:2,dataCodewordsPerBlock:16}]}]},{infoBits:48118,versionNumber:11,alignmentPatternCenters:[6,30,54],errorCorrectionLevels:[{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:81}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:1,
dataCodewordsPerBlock:50},{numBlocks:4,dataCodewordsPerBlock:51}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:22},{numBlocks:4,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:12},{numBlocks:8,dataCodewordsPerBlock:13}]}]},{infoBits:51042,versionNumber:12,alignmentPatternCenters:[6,32,58],errorCorrectionLevels:[{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:92},{numBlocks:2,dataCodewordsPerBlock:93}]},
{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:36},{numBlocks:2,dataCodewordsPerBlock:37}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:20},{numBlocks:6,dataCodewordsPerBlock:21}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:14},{numBlocks:4,dataCodewordsPerBlock:15}]}]},{infoBits:55367,versionNumber:13,alignmentPatternCenters:[6,34,62],errorCorrectionLevels:[{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:107}]},
{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:37},{numBlocks:1,dataCodewordsPerBlock:38}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:20},{numBlocks:4,dataCodewordsPerBlock:21}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:12,dataCodewordsPerBlock:11},{numBlocks:4,dataCodewordsPerBlock:12}]}]},{infoBits:58893,versionNumber:14,alignmentPatternCenters:[6,26,46,66],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:115},
{numBlocks:1,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:40},{numBlocks:5,dataCodewordsPerBlock:41}]},{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:16},{numBlocks:5,dataCodewordsPerBlock:17}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:12},{numBlocks:5,dataCodewordsPerBlock:13}]}]},{infoBits:63784,versionNumber:15,alignmentPatternCenters:[6,26,48,70],errorCorrectionLevels:[{ecCodewordsPerBlock:22,
ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:87},{numBlocks:1,dataCodewordsPerBlock:88}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:41},{numBlocks:5,dataCodewordsPerBlock:42}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:24},{numBlocks:7,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:12},{numBlocks:7,dataCodewordsPerBlock:13}]}]},{infoBits:68472,versionNumber:16,alignmentPatternCenters:[6,26,50,
74],errorCorrectionLevels:[{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:98},{numBlocks:1,dataCodewordsPerBlock:99}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:45},{numBlocks:3,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:15,dataCodewordsPerBlock:19},{numBlocks:2,dataCodewordsPerBlock:20}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:15},{numBlocks:13,dataCodewordsPerBlock:16}]}]},{infoBits:70749,
versionNumber:17,alignmentPatternCenters:[6,30,54,78],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:107},{numBlocks:5,dataCodewordsPerBlock:108}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:46},{numBlocks:1,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:22},{numBlocks:15,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:14},{numBlocks:17,
dataCodewordsPerBlock:15}]}]},{infoBits:76311,versionNumber:18,alignmentPatternCenters:[6,30,56,82],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:120},{numBlocks:1,dataCodewordsPerBlock:121}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:9,dataCodewordsPerBlock:43},{numBlocks:4,dataCodewordsPerBlock:44}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:22},{numBlocks:1,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:2,
dataCodewordsPerBlock:14},{numBlocks:19,dataCodewordsPerBlock:15}]}]},{infoBits:79154,versionNumber:19,alignmentPatternCenters:[6,30,58,86],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:113},{numBlocks:4,dataCodewordsPerBlock:114}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:44},{numBlocks:11,dataCodewordsPerBlock:45}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:21},{numBlocks:4,dataCodewordsPerBlock:22}]},
{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:9,dataCodewordsPerBlock:13},{numBlocks:16,dataCodewordsPerBlock:14}]}]},{infoBits:84390,versionNumber:20,alignmentPatternCenters:[6,34,62,90],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:107},{numBlocks:5,dataCodewordsPerBlock:108}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:41},{numBlocks:13,dataCodewordsPerBlock:42}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:15,dataCodewordsPerBlock:24},
{numBlocks:5,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:15,dataCodewordsPerBlock:15},{numBlocks:10,dataCodewordsPerBlock:16}]}]},{infoBits:87683,versionNumber:21,alignmentPatternCenters:[6,28,50,72,94],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:116},{numBlocks:4,dataCodewordsPerBlock:117}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:42}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:22},
{numBlocks:6,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:16},{numBlocks:6,dataCodewordsPerBlock:17}]}]},{infoBits:92361,versionNumber:22,alignmentPatternCenters:[6,26,50,74,98],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:111},{numBlocks:7,dataCodewordsPerBlock:112}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:24},
{numBlocks:16,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:34,dataCodewordsPerBlock:13}]}]},{infoBits:96236,versionNumber:23,alignmentPatternCenters:[6,30,54,74,102],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:121},{numBlocks:5,dataCodewordsPerBlock:122}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:47},{numBlocks:14,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:24},
{numBlocks:14,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:16,dataCodewordsPerBlock:15},{numBlocks:14,dataCodewordsPerBlock:16}]}]},{infoBits:102084,versionNumber:24,alignmentPatternCenters:[6,28,54,80,106],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:117},{numBlocks:4,dataCodewordsPerBlock:118}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:45},{numBlocks:14,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,
ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:24},{numBlocks:16,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:30,dataCodewordsPerBlock:16},{numBlocks:2,dataCodewordsPerBlock:17}]}]},{infoBits:102881,versionNumber:25,alignmentPatternCenters:[6,32,58,84,110],errorCorrectionLevels:[{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:106},{numBlocks:4,dataCodewordsPerBlock:107}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:47},{numBlocks:13,
dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:24},{numBlocks:22,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:22,dataCodewordsPerBlock:15},{numBlocks:13,dataCodewordsPerBlock:16}]}]},{infoBits:110507,versionNumber:26,alignmentPatternCenters:[6,30,58,86,114],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:114},{numBlocks:2,dataCodewordsPerBlock:115}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:19,
dataCodewordsPerBlock:46},{numBlocks:4,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:28,dataCodewordsPerBlock:22},{numBlocks:6,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:33,dataCodewordsPerBlock:16},{numBlocks:4,dataCodewordsPerBlock:17}]}]},{infoBits:110734,versionNumber:27,alignmentPatternCenters:[6,34,62,90,118],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:122},{numBlocks:4,dataCodewordsPerBlock:123}]},
{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:22,dataCodewordsPerBlock:45},{numBlocks:3,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:23},{numBlocks:26,dataCodewordsPerBlock:24}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:12,dataCodewordsPerBlock:15},{numBlocks:28,dataCodewordsPerBlock:16}]}]},{infoBits:117786,versionNumber:28,alignmentPatternCenters:[6,26,50,74,98,122],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:117},
{numBlocks:10,dataCodewordsPerBlock:118}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:45},{numBlocks:23,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:24},{numBlocks:31,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:15},{numBlocks:31,dataCodewordsPerBlock:16}]}]},{infoBits:119615,versionNumber:29,alignmentPatternCenters:[6,30,54,78,102,126],errorCorrectionLevels:[{ecCodewordsPerBlock:30,
ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:116},{numBlocks:7,dataCodewordsPerBlock:117}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:21,dataCodewordsPerBlock:45},{numBlocks:7,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:23},{numBlocks:37,dataCodewordsPerBlock:24}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:15},{numBlocks:26,dataCodewordsPerBlock:16}]}]},{infoBits:126325,versionNumber:30,alignmentPatternCenters:[6,
26,52,78,104,130],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:115},{numBlocks:10,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:47},{numBlocks:10,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:15,dataCodewordsPerBlock:24},{numBlocks:25,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:23,dataCodewordsPerBlock:15},{numBlocks:25,dataCodewordsPerBlock:16}]}]},
{infoBits:127568,versionNumber:31,alignmentPatternCenters:[6,30,56,82,108,134],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:13,dataCodewordsPerBlock:115},{numBlocks:3,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:46},{numBlocks:29,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:42,dataCodewordsPerBlock:24},{numBlocks:1,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:23,dataCodewordsPerBlock:15},
{numBlocks:28,dataCodewordsPerBlock:16}]}]},{infoBits:133589,versionNumber:32,alignmentPatternCenters:[6,34,60,86,112,138],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:115}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:46},{numBlocks:23,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:24},{numBlocks:35,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:19,
dataCodewordsPerBlock:15},{numBlocks:35,dataCodewordsPerBlock:16}]}]},{infoBits:136944,versionNumber:33,alignmentPatternCenters:[6,30,58,86,114,142],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:115},{numBlocks:1,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:14,dataCodewordsPerBlock:46},{numBlocks:21,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:29,dataCodewordsPerBlock:24},{numBlocks:19,dataCodewordsPerBlock:25}]},
{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:15},{numBlocks:46,dataCodewordsPerBlock:16}]}]},{infoBits:141498,versionNumber:34,alignmentPatternCenters:[6,34,62,90,118,146],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:13,dataCodewordsPerBlock:115},{numBlocks:6,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:14,dataCodewordsPerBlock:46},{numBlocks:23,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:44,
dataCodewordsPerBlock:24},{numBlocks:7,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:59,dataCodewordsPerBlock:16},{numBlocks:1,dataCodewordsPerBlock:17}]}]},{infoBits:145311,versionNumber:35,alignmentPatternCenters:[6,30,54,78,102,126,150],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:12,dataCodewordsPerBlock:121},{numBlocks:7,dataCodewordsPerBlock:122}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:12,dataCodewordsPerBlock:47},{numBlocks:26,dataCodewordsPerBlock:48}]},
{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:39,dataCodewordsPerBlock:24},{numBlocks:14,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:22,dataCodewordsPerBlock:15},{numBlocks:41,dataCodewordsPerBlock:16}]}]},{infoBits:150283,versionNumber:36,alignmentPatternCenters:[6,24,50,76,102,128,154],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:121},{numBlocks:14,dataCodewordsPerBlock:122}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:6,
dataCodewordsPerBlock:47},{numBlocks:34,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:46,dataCodewordsPerBlock:24},{numBlocks:10,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:15},{numBlocks:64,dataCodewordsPerBlock:16}]}]},{infoBits:152622,versionNumber:37,alignmentPatternCenters:[6,28,54,80,106,132,158],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:122},{numBlocks:4,dataCodewordsPerBlock:123}]},
{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:29,dataCodewordsPerBlock:46},{numBlocks:14,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:49,dataCodewordsPerBlock:24},{numBlocks:10,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:24,dataCodewordsPerBlock:15},{numBlocks:46,dataCodewordsPerBlock:16}]}]},{infoBits:158308,versionNumber:38,alignmentPatternCenters:[6,32,58,84,110,136,162],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:4,
dataCodewordsPerBlock:122},{numBlocks:18,dataCodewordsPerBlock:123}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:13,dataCodewordsPerBlock:46},{numBlocks:32,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:48,dataCodewordsPerBlock:24},{numBlocks:14,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:42,dataCodewordsPerBlock:15},{numBlocks:32,dataCodewordsPerBlock:16}]}]},{infoBits:161089,versionNumber:39,alignmentPatternCenters:[6,26,54,82,110,138,166],
errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:20,dataCodewordsPerBlock:117},{numBlocks:4,dataCodewordsPerBlock:118}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:40,dataCodewordsPerBlock:47},{numBlocks:7,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:43,dataCodewordsPerBlock:24},{numBlocks:22,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:15},{numBlocks:67,dataCodewordsPerBlock:16}]}]},{infoBits:167017,
versionNumber:40,alignmentPatternCenters:[6,30,58,86,114,142,170],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:118},{numBlocks:6,dataCodewordsPerBlock:119}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:18,dataCodewordsPerBlock:47},{numBlocks:31,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:34,dataCodewordsPerBlock:24},{numBlocks:34,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:20,dataCodewordsPerBlock:15},
{numBlocks:61,dataCodewordsPerBlock:16}]}]}];function J(a,b){a^=b;for(b=0;a;)b++,a&=a-1;return b}function K(a,b){return b<<1|a}
let ia=[{bits:21522,formatInfo:{errorCorrectionLevel:1,dataMask:0}},{bits:20773,formatInfo:{errorCorrectionLevel:1,dataMask:1}},{bits:24188,formatInfo:{errorCorrectionLevel:1,dataMask:2}},{bits:23371,formatInfo:{errorCorrectionLevel:1,dataMask:3}},{bits:17913,formatInfo:{errorCorrectionLevel:1,dataMask:4}},{bits:16590,formatInfo:{errorCorrectionLevel:1,dataMask:5}},{bits:20375,formatInfo:{errorCorrectionLevel:1,dataMask:6}},{bits:19104,formatInfo:{errorCorrectionLevel:1,dataMask:7}},{bits:30660,formatInfo:{errorCorrectionLevel:0,
dataMask:0}},{bits:29427,formatInfo:{errorCorrectionLevel:0,dataMask:1}},{bits:32170,formatInfo:{errorCorrectionLevel:0,dataMask:2}},{bits:30877,formatInfo:{errorCorrectionLevel:0,dataMask:3}},{bits:26159,formatInfo:{errorCorrectionLevel:0,dataMask:4}},{bits:25368,formatInfo:{errorCorrectionLevel:0,dataMask:5}},{bits:27713,formatInfo:{errorCorrectionLevel:0,dataMask:6}},{bits:26998,formatInfo:{errorCorrectionLevel:0,dataMask:7}},{bits:5769,formatInfo:{errorCorrectionLevel:3,dataMask:0}},{bits:5054,
formatInfo:{errorCorrectionLevel:3,dataMask:1}},{bits:7399,formatInfo:{errorCorrectionLevel:3,dataMask:2}},{bits:6608,formatInfo:{errorCorrectionLevel:3,dataMask:3}},{bits:1890,formatInfo:{errorCorrectionLevel:3,dataMask:4}},{bits:597,formatInfo:{errorCorrectionLevel:3,dataMask:5}},{bits:3340,formatInfo:{errorCorrectionLevel:3,dataMask:6}},{bits:2107,formatInfo:{errorCorrectionLevel:3,dataMask:7}},{bits:13663,formatInfo:{errorCorrectionLevel:2,dataMask:0}},{bits:12392,formatInfo:{errorCorrectionLevel:2,
dataMask:1}},{bits:16177,formatInfo:{errorCorrectionLevel:2,dataMask:2}},{bits:14854,formatInfo:{errorCorrectionLevel:2,dataMask:3}},{bits:9396,formatInfo:{errorCorrectionLevel:2,dataMask:4}},{bits:8579,formatInfo:{errorCorrectionLevel:2,dataMask:5}},{bits:11994,formatInfo:{errorCorrectionLevel:2,dataMask:6}},{bits:11245,formatInfo:{errorCorrectionLevel:2,dataMask:7}}],ja=[a=>0===(a.y+a.x)%2,a=>0===a.y%2,a=>0===a.x%3,a=>0===(a.y+a.x)%3,a=>0===(Math.floor(a.y/2)+Math.floor(a.x/3))%2,a=>0===a.x*a.y%
2+a.x*a.y%3,a=>0===(a.y*a.x%2+a.y*a.x%3)%2,a=>0===((a.y+a.x)%2+a.y*a.x%3)%2];
function ka(a,b,c){c=ja[c.dataMask];let d=a.height;var e=17+4*b.versionNumber;let f=x.createEmpty(e,e);f.setRegion(0,0,9,9,!0);f.setRegion(e-8,0,8,9,!0);f.setRegion(0,e-8,9,8,!0);for(var g of b.alignmentPatternCenters)for(var h of b.alignmentPatternCenters)6===g&&6===h||6===g&&h===e-7||g===e-7&&6===h||f.setRegion(g-2,h-2,5,5,!0);f.setRegion(6,9,1,e-17,!0);f.setRegion(9,6,e-17,1,!0);6<b.versionNumber&&(f.setRegion(e-11,0,3,6,!0),f.setRegion(0,e-11,6,3,!0));b=[];h=g=0;e=!0;for(let k=d-1;0<k;k-=2){6===
k&&k--;for(let m=0;m<d;m++){let l=e?d-1-m:m;for(let n=0;2>n;n++){let q=k-n;if(!f.get(q,l)){h++;let r=a.get(q,l);c({y:l,x:q})&&(r=!r);g=g<<1|r;8===h&&(b.push(g),g=h=0)}}}e=!e}return b}
function la(a){var b=a.height,c=Math.floor((b-17)/4);if(6>=c)return I[c-1];c=0;for(var d=5;0<=d;d--)for(var e=b-9;e>=b-11;e--)c=K(a.get(e,d),c);d=0;for(e=5;0<=e;e--)for(let g=b-9;g>=b-11;g--)d=K(a.get(e,g),d);a=Infinity;let f;for(let g of I){if(g.infoBits===c||g.infoBits===d)return g;b=J(c,g.infoBits);b<a&&(f=g,a=b);b=J(d,g.infoBits);b<a&&(f=g,a=b)}if(3>=a)return f}
function ma(a){let b=0;for(var c=0;8>=c;c++)6!==c&&(b=K(a.get(c,8),b));for(c=7;0<=c;c--)6!==c&&(b=K(a.get(8,c),b));var d=a.height;c=0;for(var e=d-1;e>=d-7;e--)c=K(a.get(8,e),c);for(e=d-8;e<d;e++)c=K(a.get(e,8),c);a=Infinity;d=null;for(let {bits:f,formatInfo:g}of ia){if(f===b||f===c)return g;e=J(b,f);e<a&&(d=g,a=e);b!==c&&(e=J(c,f),e<a&&(d=g,a=e))}return 3>=a?d:null}
function na(a,b,c){let d=b.errorCorrectionLevels[c],e=[],f=0;d.ecBlocks.forEach(h=>{for(let k=0;k<h.numBlocks;k++)e.push({numDataCodewords:h.dataCodewordsPerBlock,codewords:[]}),f+=h.dataCodewordsPerBlock+d.ecCodewordsPerBlock});if(a.length<f)return null;a=a.slice(0,f);b=d.ecBlocks[0].dataCodewordsPerBlock;for(c=0;c<b;c++)for(var g of e)g.codewords.push(a.shift());if(1<d.ecBlocks.length)for(g=d.ecBlocks[0].numBlocks,b=d.ecBlocks[1].numBlocks,c=0;c<b;c++)e[g+c].codewords.push(a.shift());for(;0<a.length;)for(let h of e)h.codewords.push(a.shift());
return e}function L(a){let b=la(a);if(!b)return null;var c=ma(a);if(!c)return null;a=ka(a,b,c);var d=na(a,b,c.errorCorrectionLevel);if(!d)return null;c=d.reduce((e,f)=>e+f.numDataCodewords,0);c=new Uint8ClampedArray(c);a=0;for(let e of d){d=ha(e.codewords,e.codewords.length-e.numDataCodewords);if(!d)return null;for(let f=0;f<e.numDataCodewords;f++)c[a++]=d[f]}try{return da(c,b.versionNumber)}catch(e){return null}}
function M(a,b,c,d){var e=a.x-b.x+c.x-d.x;let f=a.y-b.y+c.y-d.y;if(0===e&&0===f)return{a11:b.x-a.x,a12:b.y-a.y,a13:0,a21:c.x-b.x,a22:c.y-b.y,a23:0,a31:a.x,a32:a.y,a33:1};let g=b.x-c.x;var h=d.x-c.x;let k=b.y-c.y,m=d.y-c.y;c=g*m-h*k;h=(e*m-h*f)/c;e=(g*f-e*k)/c;return{a11:b.x-a.x+h*b.x,a12:b.y-a.y+h*b.y,a13:h,a21:d.x-a.x+e*d.x,a22:d.y-a.y+e*d.y,a23:e,a31:a.x,a32:a.y,a33:1}}
function oa(a,b,c,d){a=M(a,b,c,d);return{a11:a.a22*a.a33-a.a23*a.a32,a12:a.a13*a.a32-a.a12*a.a33,a13:a.a12*a.a23-a.a13*a.a22,a21:a.a23*a.a31-a.a21*a.a33,a22:a.a11*a.a33-a.a13*a.a31,a23:a.a13*a.a21-a.a11*a.a23,a31:a.a21*a.a32-a.a22*a.a31,a32:a.a12*a.a31-a.a11*a.a32,a33:a.a11*a.a22-a.a12*a.a21}}
function pa(a,b){var c=oa({x:3.5,y:3.5},{x:b.dimension-3.5,y:3.5},{x:b.dimension-6.5,y:b.dimension-6.5},{x:3.5,y:b.dimension-3.5}),d=M(b.topLeft,b.topRight,b.alignmentPattern,b.bottomLeft),e=d.a11*c.a11+d.a21*c.a12+d.a31*c.a13,f=d.a12*c.a11+d.a22*c.a12+d.a32*c.a13,g=d.a13*c.a11+d.a23*c.a12+d.a33*c.a13,h=d.a11*c.a21+d.a21*c.a22+d.a31*c.a23,k=d.a12*c.a21+d.a22*c.a22+d.a32*c.a23,m=d.a13*c.a21+d.a23*c.a22+d.a33*c.a23,l=d.a11*c.a31+d.a21*c.a32+d.a31*c.a33,n=d.a12*c.a31+d.a22*c.a32+d.a32*c.a33,q=d.a13*
c.a31+d.a23*c.a32+d.a33*c.a33;c=x.createEmpty(b.dimension,b.dimension);d=(r,u)=>{const p=g*r+m*u+q;return{x:(e*r+h*u+l)/p,y:(f*r+k*u+n)/p}};for(let r=0;r<b.dimension;r++)for(let u=0;u<b.dimension;u++){let p=d(u+.5,r+.5);c.set(u,r,a.get(Math.floor(p.x),Math.floor(p.y)))}return{matrix:c,mappingFunction:d}}let N=(a,b)=>Math.sqrt(Math.pow(b.x-a.x,2)+Math.pow(b.y-a.y,2));function O(a){return a.reduce((b,c)=>b+c)}
function qa(a,b,c){let d=N(a,b),e=N(b,c),f=N(a,c),g,h,k;e>=d&&e>=f?[g,h,k]=[b,a,c]:f>=e&&f>=d?[g,h,k]=[a,b,c]:[g,h,k]=[a,c,b];0>(k.x-h.x)*(g.y-h.y)-(k.y-h.y)*(g.x-h.x)&&([g,k]=[k,g]);return{bottomLeft:g,topLeft:h,topRight:k}}
function ra(a,b,c,d){d=(O(P(a,c,d,5))/7+O(P(a,b,d,5))/7+O(P(c,a,d,5))/7+O(P(b,a,d,5))/7)/4;if(1>d)throw Error("Invalid module size");b=Math.round(N(a,b)/d);a=Math.round(N(a,c)/d);a=Math.floor((b+a)/2)+7;switch(a%4){case 0:a++;break;case 2:a--}return{dimension:a,moduleSize:d}}
function Q(a,b,c,d){let e=[{x:Math.floor(a.x),y:Math.floor(a.y)}];var f=Math.abs(b.y-a.y)>Math.abs(b.x-a.x);if(f){var g=Math.floor(a.y);var h=Math.floor(a.x);a=Math.floor(b.y);b=Math.floor(b.x)}else g=Math.floor(a.x),h=Math.floor(a.y),a=Math.floor(b.x),b=Math.floor(b.y);let k=Math.abs(a-g),m=Math.abs(b-h),l=Math.floor(-k/2),n=g<a?1:-1,q=h<b?1:-1,r=!0;for(let u=g,p=h;u!==a+n;u+=n){g=f?p:u;h=f?u:p;if(c.get(g,h)!==r&&(r=!r,e.push({x:g,y:h}),e.length===d+1))break;l+=m;if(0<l){if(p===b)break;p+=q;l-=k}}c=
[];for(f=0;f<d;f++)e[f]&&e[f+1]?c.push(N(e[f],e[f+1])):c.push(0);return c}function P(a,b,c,d){let e=b.y-a.y,f=b.x-a.x;b=Q(a,b,c,Math.ceil(d/2));a=Q(a,{x:a.x-f,y:a.y-e},c,Math.ceil(d/2));c=b.shift()+a.shift()-1;return a.concat(c).concat(...b)}function R(a,b){let c=O(a)/O(b),d=0;b.forEach((e,f)=>{d+=Math.pow(a[f]-e*c,2)});return{averageSize:c,error:d}}
function S(a,b,c){try{let d=P(a,{x:-1,y:a.y},c,b.length),e=P(a,{x:a.x,y:-1},c,b.length),f=P(a,{x:Math.max(0,a.x-a.y)-1,y:Math.max(0,a.y-a.x)-1},c,b.length),g=P(a,{x:Math.min(c.width,a.x+a.y)+1,y:Math.min(c.height,a.y+a.x)+1},c,b.length),h=R(d,b),k=R(e,b),m=R(f,b),l=R(g,b),n=(h.averageSize+k.averageSize+m.averageSize+l.averageSize)/4;return Math.sqrt(h.error*h.error+k.error*k.error+m.error*m.error+l.error*l.error)+(Math.pow(h.averageSize-n,2)+Math.pow(k.averageSize-n,2)+Math.pow(m.averageSize-n,2)+
Math.pow(l.averageSize-n,2))/n}catch(d){return Infinity}}function T(a,b){for(var c=Math.round(b.x);a.get(c,Math.round(b.y));)c--;for(var d=Math.round(b.x);a.get(d,Math.round(b.y));)d++;c=(c+d)/2;for(d=Math.round(b.y);a.get(Math.round(c),d);)d--;for(b=Math.round(b.y);a.get(Math.round(c),b);)b++;return{x:c,y:(d+b)/2}}
function sa(a){var b=[],c=[];let d=[];var e=[];for(let p=0;p<=a.height;p++){var f=0,g=!1;let t=[0,0,0,0,0];for(let v=-1;v<=a.width;v++){var h=a.get(v,p);if(h===g)f++;else{t=[t[1],t[2],t[3],t[4],f];f=1;g=h;var k=O(t)/7;k=Math.abs(t[0]-k)<k&&Math.abs(t[1]-k)<k&&Math.abs(t[2]-3*k)<3*k&&Math.abs(t[3]-k)<k&&Math.abs(t[4]-k)<k&&!h;var m=O(t.slice(-3))/3;h=Math.abs(t[2]-m)<m&&Math.abs(t[3]-m)<m&&Math.abs(t[4]-m)<m&&h;if(k){let z=v-t[3]-t[4],y=z-t[2];k={startX:y,endX:z,y:p};m=c.filter(w=>y>=w.bottom.startX&&
y<=w.bottom.endX||z>=w.bottom.startX&&y<=w.bottom.endX||y<=w.bottom.startX&&z>=w.bottom.endX&&1.5>t[2]/(w.bottom.endX-w.bottom.startX)&&.5<t[2]/(w.bottom.endX-w.bottom.startX));0<m.length?m[0].bottom=k:c.push({top:k,bottom:k})}if(h){let z=v-t[4],y=z-t[3];h={startX:y,y:p,endX:z};k=e.filter(w=>y>=w.bottom.startX&&y<=w.bottom.endX||z>=w.bottom.startX&&y<=w.bottom.endX||y<=w.bottom.startX&&z>=w.bottom.endX&&1.5>t[2]/(w.bottom.endX-w.bottom.startX)&&.5<t[2]/(w.bottom.endX-w.bottom.startX));0<k.length?
k[0].bottom=h:e.push({top:h,bottom:h})}}}b.push(...c.filter(v=>v.bottom.y!==p&&2<=v.bottom.y-v.top.y));c=c.filter(v=>v.bottom.y===p);d.push(...e.filter(v=>v.bottom.y!==p));e=e.filter(v=>v.bottom.y===p)}b.push(...c.filter(p=>2<=p.bottom.y-p.top.y));d.push(...e);c=[];for(var l of b)2>l.bottom.y-l.top.y||(b=(l.top.startX+l.top.endX+l.bottom.startX+l.bottom.endX)/4,e=(l.top.y+l.bottom.y+1)/2,a.get(Math.round(b),Math.round(e))&&(f=[l.top.endX-l.top.startX,l.bottom.endX-l.bottom.startX,l.bottom.y-l.top.y+
1],f=O(f)/f.length,g=S({x:Math.round(b),y:Math.round(e)},[1,1,3,1,1],a),c.push({score:g,x:b,y:e,size:f})));if(3>c.length)return null;c.sort((p,t)=>p.score-t.score);l=[];for(b=0;b<Math.min(c.length,5);++b){e=c[b];f=[];for(var n of c)n!==e&&f.push(Object.assign(Object.assign({},n),{score:n.score+Math.pow(n.size-e.size,2)/e.size}));f.sort((p,t)=>p.score-t.score);l.push({points:[e,f[0],f[1]],score:e.score+f[0].score+f[1].score})}l.sort((p,t)=>p.score-t.score);let {topRight:q,topLeft:r,bottomLeft:u}=qa(...l[0].points);
l=U(a,d,q,r,u);n=[];l&&n.push({alignmentPattern:{x:l.alignmentPattern.x,y:l.alignmentPattern.y},bottomLeft:{x:u.x,y:u.y},dimension:l.dimension,topLeft:{x:r.x,y:r.y},topRight:{x:q.x,y:q.y}});l=T(a,q);b=T(a,r);c=T(a,u);(a=U(a,d,l,b,c))&&n.push({alignmentPattern:{x:a.alignmentPattern.x,y:a.alignmentPattern.y},bottomLeft:{x:c.x,y:c.y},topLeft:{x:b.x,y:b.y},topRight:{x:l.x,y:l.y},dimension:a.dimension});return 0===n.length?null:n}
function U(a,b,c,d,e){let f,g;try{({dimension:f,moduleSize:g}=ra(d,c,e,a))}catch(l){return null}var h=c.x-d.x+e.x,k=c.y-d.y+e.y;c=(N(d,e)+N(d,c))/2/g;e=1-3/c;let m={x:d.x+e*(h-d.x),y:d.y+e*(k-d.y)};b=b.map(l=>{const n=(l.top.startX+l.top.endX+l.bottom.startX+l.bottom.endX)/4;l=(l.top.y+l.bottom.y+1)/2;if(a.get(Math.floor(n),Math.floor(l))){var q=S({x:Math.floor(n),y:Math.floor(l)},[1,1,1],a)+N({x:n,y:l},m);return{x:n,y:l,score:q}}}).filter(l=>!!l).sort((l,n)=>l.score-n.score);return{alignmentPattern:15<=
c&&b.length?b[0]:m,dimension:f}}
function V(a){var b=sa(a);if(!b)return null;for(let e of b){b=pa(a,e);var c=b.matrix;if(null==c)c=null;else{var d=L(c);if(d)c=d;else{for(d=0;d<c.width;d++)for(let f=d+1;f<c.height;f++)c.get(d,f)!==c.get(f,d)&&(c.set(d,f,!c.get(d,f)),c.set(f,d,!c.get(f,d)));c=L(c)}}if(c)return{binaryData:c.bytes,data:c.text,chunks:c.chunks,version:c.version,location:{topRightCorner:b.mappingFunction(e.dimension,0),topLeftCorner:b.mappingFunction(0,0),bottomRightCorner:b.mappingFunction(e.dimension,e.dimension),bottomLeftCorner:b.mappingFunction(0,
e.dimension),topRightFinderPattern:e.topRight,topLeftFinderPattern:e.topLeft,bottomLeftFinderPattern:e.bottomLeft,bottomRightAlignmentPattern:e.alignmentPattern},matrix:b.matrix}}return null}let ta={inversionAttempts:"attemptBoth",greyScaleWeights:{red:.2126,green:.7152,blue:.0722,useIntegerApproximation:!1},canOverwriteImage:!0};function W(a,b){Object.keys(b).forEach(c=>{a[c]=b[c]})}
function X(a,b,c,d={}){let e=Object.create(null);W(e,ta);W(e,d);d="onlyInvert"===e.inversionAttempts||"invertFirst"===e.inversionAttempts;var f="attemptBoth"===e.inversionAttempts||d;var g=e.greyScaleWeights,h=e.canOverwriteImage,k=b*c;if(a.length!==4*k)throw Error("Malformed data passed to binarizer.");var m=0;if(h){var l=new Uint8ClampedArray(a.buffer,m,k);m+=k}l=new A(b,c,l);if(g.useIntegerApproximation)for(var n=0;n<c;n++)for(var q=0;q<b;q++){var r=4*(n*b+q);l.set(q,n,g.red*a[r]+g.green*a[r+1]+
g.blue*a[r+2]+128>>8)}else for(n=0;n<c;n++)for(q=0;q<b;q++)r=4*(n*b+q),l.set(q,n,g.red*a[r]+g.green*a[r+1]+g.blue*a[r+2]);g=Math.ceil(b/8);n=Math.ceil(c/8);q=g*n;if(h){var u=new Uint8ClampedArray(a.buffer,m,q);m+=q}u=new A(g,n,u);for(q=0;q<n;q++)for(r=0;r<g;r++){var p=Infinity,t=0;for(var v=0;8>v;v++)for(let w=0;8>w;w++){let aa=l.get(8*r+w,8*q+v);p=Math.min(p,aa);t=Math.max(t,aa)}v=(p+t)/2;v=Math.min(255,1.11*v);24>=t-p&&(v=p/2,0<q&&0<r&&(t=(u.get(r,q-1)+2*u.get(r-1,q)+u.get(r-1,q-1))/4,p<t&&(v=t)));
u.set(r,q,v)}h?(q=new Uint8ClampedArray(a.buffer,m,k),m+=k,q=new x(q,b)):q=x.createEmpty(b,c);r=null;f&&(h?(a=new Uint8ClampedArray(a.buffer,m,k),r=new x(a,b)):r=x.createEmpty(b,c));for(b=0;b<n;b++)for(a=0;a<g;a++){c=g-3;c=2>a?2:a>c?c:a;h=n-3;h=2>b?2:b>h?h:b;k=0;for(m=-2;2>=m;m++)for(p=-2;2>=p;p++)k+=u.get(c+m,h+p);c=k/25;for(h=0;8>h;h++)for(k=0;8>k;k++)m=8*a+h,p=8*b+k,t=l.get(m,p),q.set(m,p,t<=c),f&&r.set(m,p,!(t<=c))}f=f?{binarized:q,inverted:r}:{binarized:q};let {binarized:z,inverted:y}=f;(f=V(d?
y:z))||"attemptBoth"!==e.inversionAttempts&&"invertFirst"!==e.inversionAttempts||(f=V(d?z:y));return f}X.default=X;let Y="dontInvert",Z={red:77,green:150,blue:29,useIntegerApproximation:!0};
self.onmessage=a=>{let b=a.data.id,c=a.data.data;switch(a.data.type){case "decode":(a=X(c.data,c.width,c.height,{inversionAttempts:Y,greyScaleWeights:Z}))?self.postMessage({id:b,type:"qrResult",data:a.data,cornerPoints:[a.location.topLeftCorner,a.location.topRightCorner,a.location.bottomRightCorner,a.location.bottomLeftCorner]}):self.postMessage({id:b,type:"qrResult",data:null});break;case "grayscaleWeights":Z.red=c.red;Z.green=c.green;Z.blue=c.blue;Z.useIntegerApproximation=c.useIntegerApproximation;
break;case "inversionMode":switch(c){case "original":Y="dontInvert";break;case "invert":Y="onlyInvert";break;case "both":Y="attemptBoth";break;default:throw Error("Invalid inversion mode");}break;case "close":self.close()}}
`]),{type:"application/javascript"}));
export { createWorker };

1454
ihm/sdk_client-CuF7MH2z.mjs Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

877
ihm/style/4nk.css Normal file
View File

@ -0,0 +1,877 @@
:root {
--primary-color
: #3A506B;
/* Bleu métallique */
--secondary-color
: #B0BEC5;
/* Gris acier */
--accent-color
: #D68C45;
/* Cuivre */
}
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #f4f4f4;
background-image: url(../assets/bgd.webp);
background-repeat:no-repeat;
background-size: cover;
background-blend-mode :soft-light;
height: 100vh;
}
.message {
margin: 30px 0;
font-size: 14px;
overflow-wrap: anywhere;
}
.message strong{
font-family: "Segoe UI Emoji", "Noto Color Emoji", "Apple Color Emoji", sans-serif;
font-size: 20px;
}
/** Modal Css */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
justify-content: center;
align-items: center;
z-index: 3;
}
.modal-content {
width: 55%;
height: 30%;
background-color: white;
border-radius: 4px;
padding: 20px;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
}
.modal-title {
margin: 0;
padding-bottom: 8px;
width: 100%;
font-size: 0.9em;
border-bottom: 1px solid #ccc;
}
.confirmation-box {
/* margin-top: 20px; */
align-content: center;
width: 70%;
height: 20%;
/* padding: 20px; */
font-size: 1.5em;
color: #333333;
top: 5%;
position: relative;
}
/* Confirmation Modal Styles */
#confirmation-modal {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 1000;
}
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
}
.modal-content {
background: white;
padding: 20px;
border-radius: 8px;
width: 90%;
max-width: 500px;
max-height: 80vh;
overflow-y: auto;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.modal-confirmation {
text-align: left;
padding: 10px;
}
.modal-confirmation h3 {
margin-bottom: 15px;
color: var(--primary-color);
font-size: 1.1em;
}
.modal-confirmation p {
margin: 8px 0;
font-size: 0.9em;
line-height: 1.4;
}
.modal-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-top: 20px;
padding-top: 15px;
border-top: 1px solid #eee;
}
.modal-footer button {
padding: 8px 16px;
border-radius: 4px;
border: none;
cursor: pointer;
font-size: 0.9em;
}
.btn-primary {
background: var(--primary-color);
color: white;
}
.btn-secondary {
background: var(--secondary-color);
color: white;
}
/* Responsive adjustments */
@media only screen and (max-width: 600px) {
.modal-content {
width: 95%;
margin: 10px;
padding: 15px;
}
.modal-confirmation h3 {
font-size: 1em;
}
.modal-confirmation p {
font-size: 0.85em;
}
}
.nav-wrapper {
position: fixed;
z-index: 2;
background: radial-gradient(circle, white, var(--primary-color));
/* background-color: #CFD8DC; */
display: flex;
justify-content: flex-end;
align-items: center;
color: #37474F;
height: 9vh;
width: 100vw;
left: 0;
top: 0;
box-shadow: 0px 8px 10px -5px rgba(0, 0, 0, .2), 0px 16px 24px 2px rgba(0, 0, 0, .14), 0px 6px 30px 5px rgba(0, 0, 0, .12);
.nav-right-icons {
display: flex;
.notification-container {
position: relative;
display: inline-block;
}
.notification-bell, .burger-menu {
z-index: 3;
height: 20px;
width: 20px;
margin-right: 1rem;
}
.notification-badge {
position: absolute;
top: -.7rem;
left: -.8rem;
background-color: red;
color: white;
border-radius: 50%;
padding: 2.5px 6px;
font-size: 0.8em;
font-weight: bold;
}
}
.notification-board {
position: absolute;
width: 20rem;
min-height: 8rem;
background-color: white;
right: 0.5rem;
display: none;
border-radius: 4px;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
display: none;
.notification-element {
padding: .8rem 0;
width: 100%;
&:hover {
background-color: rgba(26, 28, 24, .08);
}
}
.notification-element:not(:last-child) {
border-bottom: 1px solid;
}
}
}
.brand-logo {
height: 100%;
width: 100vw;
align-content: center;
position: relative;
display: flex;
position: absolute;
align-items: center;
justify-content: center;
text-align: center;
font-size: 1.5em;
font-weight: bold;
}
.container {
text-align: center;
display: grid;
height: 100vh;
grid-template-columns: repeat(7, 1fr);
gap: 10px;
grid-auto-rows: 10vh 15vh 1fr;
}
.title-container {
grid-column: 2 / 7;
grid-row: 2;
}
.page-container {
grid-column: 2 / 7;
grid-row: 3 ;
justify-content: center;
display: flex;
padding: 1rem;
box-sizing: border-box;
max-height: 60vh;
}
h1 {
font-size: 2em;
margin: 20px 0;
}
@media only screen and (min-width: 600px) {
.tab-container {
display: none;
}
.page-container {
display: flex;
align-items: center;
}
.process-container {
grid-column: 3 / 6;
grid-row: 3 ;
.card {
min-width: 40vw;
}
}
.separator {
width: 2px;
background-color: #78909C;
height: 80%;
margin: 0 0.5em;
}
.tab-content {
display: flex;
flex-direction: column;
justify-content: space-evenly;
align-items: center;
height: 80%;
}
}
@media only screen and (max-width: 600px) {
.process-container {
grid-column: 2 / 7;
grid-row: 3 ;
}
.container {
grid-auto-rows: 10vh 15vh 15vh 1fr;
}
.tab-container {
grid-column: 1 / 8;
grid-row: 3;
}
.page-container {
grid-column: 2 / 7;
grid-row: 4 ;
}
.separator {
display: none;
}
.tabs {
display: flex;
flex-grow: 1;
overflow: hidden;
z-index: 1;
border-bottom-style: solid;
border-bottom-width: 1px;
border-bottom-color: #E0E4D6;
}
.tab {
flex: 1;
text-align: center;
padding: 10px 0;
cursor: pointer;
font-size: 1em;
color: #6200ea;
&:hover {
background-color: rgba(26, 28, 24, .08);
}
}
.tab.active {
border-bottom: 2px solid #6200ea;
font-weight: bold;
}
.card.tab-content {
display: none;
}
.tab-content.active {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 80%;
}
.modal-content {
width: 80%;
height: 20%;
}
}
.qr-code {
display: flex;
justify-content: center;
align-items: center;
height: 200px;
}
.emoji-display {
font-family: "Segoe UI Emoji", "Noto Color Emoji", "Apple Color Emoji", sans-serif;
font-size: 20px;
}
#emoji-display-2{
margin-top: 30px;
font-family: "Segoe UI Emoji", "Noto Color Emoji", "Apple Color Emoji", sans-serif;
font-size: 20px;
}
#okButton{
margin-bottom: 2em;
cursor: pointer;
background-color: #D0D0D7;
color: white;
border-style: none;
border-radius: 5px;
color: #000;
padding: 2px;
margin-top: 10px;
}
.pairing-request {
font-family: "Segoe UI Emoji", "Noto Color Emoji", "Apple Color Emoji", sans-serif;
font-size: 14px;
margin-top: 0px;
}
.sp-address-btn {
margin-bottom: 2em;
cursor: pointer;
background-color: #D0D0D7;
color: white;
border-style: none;
border-radius: 5px;
color: #000;
padding: 2px;
}
.camera-card {
display: flex;
justify-content: center;
align-items: center;
/* height: 200px; */
}
.btn {
display: inline-block;
padding: 10px 20px;
background-color: var(--primary-color);
color: white;
text-align: center;
border-radius: 5px;
cursor: pointer;
text-decoration: none;
}
.btn:hover {
background-color: #3700b3;
}
.card {
min-width: 300px;
border: 1px solid #e0e0e0;
border-radius: 8px;
background-color: white;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
box-sizing: border-box;
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
height: 60vh;
justify-content: flex-start;
padding: 1rem;
overflow-y: auto;
}
.card-content {
flex-grow: 1;
flex-direction: column;
display: flex;
justify-content: flex-start;
align-items: center;
text-align: left;
font-size: .8em;
position: relative;
left: 2vw;
width: 90%;
.process-title {
font-weight: bold;
padding: 1rem 0;
}
.process-element {
padding: .4rem 0;
&:hover {
background-color: rgba(26, 28, 24, .08);
}
&.selected {
background-color: rgba(26, 28, 24, .08);
}
}
}
.card-description {
padding: 20px;
font-size: 1em;
color: #333;
width: 90%;
height: 50px;
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 0px;
}
.card-action {
width: 100%;
}
.menu-content {
display: none;
position: absolute;
top: 3.4rem;
right: 1rem;
background-color: white;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
border-radius: 5px;
overflow: hidden;
}
.menu-content a {
display: block;
padding: 10px 20px;
text-decoration: none;
color: #333;
border-bottom: 1px solid #e0e0e0;
&:hover {
background-color: rgba(26, 28, 24, .08);
}
}
.menu-content a:last-child {
border-bottom: none;
}
.qr-code-scanner {
display: none;
}
/* QR READER */
#qr-reader div {
position: inherit;
}
#qr-reader div img{
top: 15px ;
right: 25px;
margin-top: 5px;
}
/* INPUT CSS **/
.input-container {
position: relative;
width: 100%;
background-color: #ECEFF1;
}
.input-field {
width: 36vw;
padding: 10px 0;
font-size: 1em;
border: none;
border-bottom: 1px solid #ccc;
outline: none;
background: transparent;
transition: border-color 0.3s;
}
.input-field:focus {
border-bottom: 2px solid #6200ea;
}
.input-label {
position: absolute;
margin-top: -0.5em;
top: 0;
left: 0;
padding: 10px 0;
font-size: 1em;
color: #999;
pointer-events: none;
transition: transform 0.3s, color 0.3s, font-size 0.3s;
}
.input-field:focus + .input-label,
.input-field:not(:placeholder-shown) + .input-label {
transform: translateY(-20px);
font-size: 0.8em;
color: #6200ea;
}
.input-underline {
position: absolute;
bottom: 0;
left: 50%;
width: 0;
height: 2px;
background-color: #6200ea;
transition: width 0.3s, left 0.3s;
}
.input-field:focus ~ .input-underline {
width: 100%;
left: 0;
}
.dropdown-content {
position: absolute;
flex-direction: column;
top: 100%;
left: 0;
width: 100%;
max-height: 150px;
overflow-y: auto;
border: 1px solid #ccc;
border-radius: 4px;
background-color: white;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
display: none;
z-index: 1;
}
.dropdown-content span {
padding: 10px;
cursor: pointer;
list-style: none;
}
.dropdown-content span:hover {
background-color: #f0f0f0;
}
/** AUTOCOMPLETE **/
select[data-multi-select-plugin] {
display: none !important;
}
.multi-select-component {
width: 36vw;
padding: 5px 0;
font-size: 1em;
border: none;
border-bottom: 1px solid #ccc;
outline: none;
background: transparent;
display: flex;
flex-direction: row;
height: auto;
width: 100%;
-o-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
}
.autocomplete-list {
border-radius: 4px 0px 0px 4px;
}
.multi-select-component:focus-within {
box-shadow: inset 0px 0px 0px 2px #78ABFE;
}
.multi-select-component .btn-group {
display: none !important;
}
.multiselect-native-select .multiselect-container {
width: 100%;
}
.selected-processes {
background-color: white;
padding: 0.4em;
}
.selected-wrapper {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
display: inline-block;
border: 1px solid #d9d9d9;
background-color: #ededed;
white-space: nowrap;
margin: 1px 5px 5px 0;
height: 22px;
vertical-align: top;
cursor: default;
}
.selected-wrapper .selected-label {
max-width: 514px;
display: inline-block;
overflow: hidden;
text-overflow: ellipsis;
padding-left: 4px;
vertical-align: top;
}
.selected-wrapper .selected-close {
display: inline-block;
text-decoration: none;
font-size: 14px;
line-height: 1.49em;
margin-left: 5px;
padding-bottom: 10px;
height: 100%;
vertical-align: top;
padding-right: 4px;
opacity: 0.2;
color: #000;
text-shadow: 0 1px 0 #fff;
font-weight: 700;
}
.search-container {
display: flex;
flex-direction: row;
}
.search-container .selected-input {
background: none;
border: 0;
height: 20px;
width: 60px;
padding: 0;
margin-bottom: 6px;
-webkit-box-shadow: none;
box-shadow: none;
}
.search-container .selected-input:focus {
outline: none;
}
.dropdown-icon.active {
transform: rotateX(180deg)
}
.search-container .dropdown-icon {
display: inline-block;
padding: 10px 5px;
position: absolute;
top: 5px;
right: 5px;
width: 10px;
height: 10px;
border: 0 !important;
/* needed */
-webkit-appearance: none;
-moz-appearance: none;
/* SVG background image */
background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2212%22%20height%3D%2212%22%20viewBox%3D%220%200%2012%2012%22%3E%3Ctitle%3Edown-arrow%3C%2Ftitle%3E%3Cg%20fill%3D%22%23818181%22%3E%3Cpath%20d%3D%22M10.293%2C3.293%2C6%2C7.586%2C1.707%2C3.293A1%2C1%2C0%2C0%2C0%2C.293%2C4.707l5%2C5a1%2C1%2C0%2C0%2C0%2C1.414%2C0l5-5a1%2C1%2C0%2C1%2C0-1.414-1.414Z%22%20fill%3D%22%23818181%22%3E%3C%2Fpath%3E%3C%2Fg%3E%3C%2Fsvg%3E");
background-position: center;
background-size: 10px;
background-repeat: no-repeat;
}
.search-container ul {
position: absolute;
list-style: none;
padding: 0;
z-index: 3;
margin-top: 29px;
width: 100%;
right: 0px;
background: #fff;
border: 1px solid #ccc;
border-top: none;
border-bottom: none;
-webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
}
.search-container ul :focus {
outline: none;
}
.search-container ul li {
display: block;
text-align: left;
padding: 8px 29px 2px 12px;
border-bottom: 1px solid #ccc;
font-size: 14px;
min-height: 31px;
}
.search-container ul li:first-child {
border-top: 1px solid #ccc;
border-radius: 4px 0px 0 0;
}
.search-container ul li:last-child {
border-radius: 4px 0px 0 0;
}
.search-container ul li:hover.not-cursor {
cursor: default;
}
.search-container ul li:hover {
color: #333;
background-color: #f0f0f0;
;
border-color: #adadad;
cursor: pointer;
}
/* Adding scrool to select options */
.autocomplete-list {
max-height: 130px;
overflow-y: auto;
}
/**************************************** Process page card ******************************************************/
.process-card {
min-width: 300px;
border: 1px solid #e0e0e0;
border-radius: 8px;
background-color: white;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
min-height: 40vh;
max-height: 60vh;
justify-content: space-between;
padding: 1rem;
overflow-y: auto;
}
.process-card-content {
text-align: left;
font-size: .8em;
position: relative;
left: 2vw;
width: 90%;
.process-title {
font-weight: bold;
padding: 1rem 0;
}
.process-element {
padding: .4rem 0;
&:hover {
background-color: rgba(26, 28, 24, .08);
}
&.selected {
background-color: rgba(26, 28, 24, .08);
}
}
.selected-process-zone {
background-color: rgba(26, 28, 24, .08);
}
}
.process-card-description {
padding: 20px;
font-size: 1em;
color: #333;
width: 90%;
}
.process-card-action {
width: 100%;
}

1507
ihm/style/account.css Normal file

File diff suppressed because it is too large Load Diff

597
ihm/style/chat.css Normal file
View File

@ -0,0 +1,597 @@
/* Styles de base */
:root {
--primary-color: #3A506B;
/* Bleu métallique */
--secondary-color: #B0BEC5;
/* Gris acier */
--accent-color: #D68C45;
/* Cuivre */
}
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}
/* 4NK NAVBAR */
.brand-logo {
text-align: center;
font-size: 1.5em;
font-weight: bold;
}
.nav-wrapper {
position: fixed;
background: radial-gradient(circle, white, var(--primary-color));
display: flex;
justify-content: space-between;
align-items: center;
color: #37474F;
height: 9vh;
width: 100vw;
left: 0;
top: 0;
box-shadow: 0px 8px 10px -5px rgba(0, 0, 0, .2), 0px 16px 24px 2px rgba(0, 0, 0, .14), 0px 6px 30px 5px rgba(0, 0, 0, .12);
}
/* Icônes de la barre de navigation */
.nav-right-icons {
display: flex;
}
.notification-bell,
.burger-menu {
height: 20px;
width: 20px;
margin-right: 1rem;
cursor: pointer;
}
.notification-container {
position: relative;
/* Conserve la position pour le notification-board */
display: inline-flex;
align-items: center;
}
.notification-board {
position: absolute;
/* Position absolue pour le placer par rapport au container */
top: 40px;
right: 0;
background-color: white;
border: 1px solid #ccc;
padding: 10px;
width: 200px;
max-height: 300px;
overflow-y: auto;
/* Scroll si les notifications dépassent la taille */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
z-index: 10;
/* Définit la priorité d'affichage au-dessus des autres éléments */
display: none;
/* Par défaut, la notification est masquée */
}
.notification-item{
cursor: pointer;
}
.notification-badge {
position: absolute;
top: -18px;
right: 35px;
background-color: red;
color: white;
border-radius: 50%;
padding: 4px 8px;
font-size: 12px;
display: none;
/* S'affiche seulement lorsqu'il y a des notifications */
z-index: 10;
}
/* Par défaut, le menu est masqué */
#menu {
display: none;
/* Menu caché par défaut */
transition: display 0.3s ease-in-out;
}
.burger-menu {
cursor: pointer;
}
/* Icône burger */
#burger-icon {
cursor: pointer;
}
.menu-content {
display: none;
position: absolute;
top: 3.4rem;
right: 1rem;
background-color: white;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
border-radius: 5px;
overflow: hidden;
}
.menu-content a {
display: block;
padding: 10px 20px;
text-decoration: none;
color: #333;
border-bottom: 1px solid #e0e0e0;
&:hover {
background-color: rgba(26, 28, 24, .08);
}
}
.menu-content a:last-child {
border-bottom: none;
}
/* Ajustement pour la barre de navigation fixe */
.container {
display: flex;
flex: 1;
height: 90vh;
margin-top: 9vh;
margin-left: -1%;
text-align: left;
width: 100vw;
}
/* Liste des groupes */
.group-list {
width: 25%;
background-color: #1f2c3d;
color: white;
padding: 20px;
box-sizing: border-box;
overflow-y: auto;
border-right: 2px solid #2c3e50;
flex-shrink: 0;
padding-right: 10px;
height: 91vh;
}
.group-list ul {
cursor: pointer;
list-style: none;
padding: 0;
padding-right: 10px;
margin-left: 20px;
}
.group-list li {
margin-bottom: 20px;
padding: 15px;
border-radius: 8px;
background-color: #273646;
cursor: pointer;
transition: background-color 0.3s, box-shadow 0.3s;
}
.group-list li:hover {
background-color: #34495e;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.group-list .member-container {
position: relative;
}
.group-list .member-container button {
margin-left: 40px;
padding: 5px;
cursor: pointer;
background: var(--primary-color);
color: white;
border: 0px solid var(--primary-color);
border-radius: 50px;
position: absolute;
top: -25px;
right: -25px;
}
.group-list .member-container button:hover {
background: var(--accent-color)
}
/* Zone de chat */
.chat-area {
display: flex;
flex-direction: column;
flex: 1;
min-width: 0;
background-color:#f1f1f1;
border-radius: 10px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
margin: 1% 0% 0.5% 1%;
}
/* En-tête du chat */
.chat-header {
background-color: #34495e;
color: white;
padding: 15px;
font-size: 20px;
font-weight: bold;
border-radius: 10px 10px 0 0;
text-align: center;
}
/* Messages */
.messages {
flex: 1;
padding: 20px;
overflow-y: auto;
background-color: #f1f1f1;
border-top: 1px solid #ddd;
}
.message-container {
display: flex;
margin: 8px;
}
.message-container .message {
align-self: flex-start;
}
.message-container .message.user {
align-self: flex-end;
margin-left: auto;
color: white;
}
.message {
max-width: 70%;
padding: 10px;
border-radius: 12px;
background:var(--secondary-color);
margin: 2px 0;
}
/* Messages de l'utilisateur */
.message.user {
background: #2196f3;
color: white;
}
.message-time {
font-size: 0.7em;
opacity: 0.7;
margin-left: 0px;
margin-top: 5px;
}
/* Amélioration de l'esthétique des messages */
/* .message.user:before {
content: '';
position: absolute;
top: 10px;
right: -10px;
border: 10px solid transparent;
border-left-color: #3498db;
} */
/* Zone de saisie */
.input-area {
padding: 10px;
background-color: #bdc3c7;
display: flex;
align-items: center;
border-radius: 10px;
margin: 1%;
/* Alignement vertical */
}
.input-area input[type="text"] {
flex: 1;
/* Prend l'espace restant */
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
}
.input-area .attachment-icon {
margin: 0 10px;
cursor: pointer;
display: flex;
align-items: center;
}
.input-area button {
padding: 10px;
margin-left: 10px;
background-color: #2980b9;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
.input-area button:hover {
background-color: #1f608d;
}
.tabs {
display: flex;
margin: 20px 0px;
gap: 10px;
}
.tabs button {
padding: 10px 20px;
cursor: pointer;
background: var(--primary-color);
color: white;
border: 0px solid var(--primary-color);
margin-right: 5px;
border-radius: 10px;
}
.tabs button:hover {
background: var(--secondary-color);
color: var(--primary-color);
}
/* Signature */
.signature-area {
display: flex;
flex-direction: column;
flex: 1;
min-width: 0;
background-color:#f1f1f1;
border-radius: 10px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
margin: 1% 0% 0.5% 1%;
transition: all 1s ease 0.1s;
visibility: visible;
}
.signature-area.hidden {
opacity: 0;
visibility: hidden;
display: none;
pointer-events: none;
}
.signature-header {
display: flex;
align-items: center;
justify-content: center;
background-color: var(--primary-color);
color: white;
border-radius: 10px 10px 0 0;
padding-left: 4%;
}
.signature-content {
padding: 10px;
background-color: var(--secondary-color);
color: var(--primary-color);
height: 100%;
border-radius: 10px;
margin: 1%;
display: flex;
flex-direction: column;
align-items: center;
}
.signature-description {
height: 20%;
width: 100%;
margin: 0% 10% 0% 10%;
overflow: auto;
display: flex;
}
.signature-description li {
margin: 1% 0% 1% 0%;
list-style: none;
padding: 2%;
border-radius: 10px;
background-color: var(--primary-color);
color: var(--secondary-color);
width: 20%;
text-align: center;
cursor: pointer;
font-weight: bold;
margin-right: 2%;
overflow: auto;
}
.signature-description li .member-list {
margin-left: -30%;
}
.signature-description li .member-list li {
width: 100%;
}
.signature-description li .member-list li:hover {
background-color: var(--secondary-color);
color: var(--primary-color);
}
.signature-documents {
height: 80%;
width: 100%;
margin: 0% 10% 0% 10%;
overflow: auto;
display: flex;
}
.signature-documents-header {
display: flex;
width: 100%;
height: 15%;
align-items: center;
}
#request-document-button {
background-color: var(--primary-color);
color: white;
border: none;
border-radius: 10px;
padding: 8px;
cursor: pointer;
margin-left: 5%;
font-weight: bold;
}
#request-document-button:hover {
background-color: var(--accent-color);
font-weight: bold;
}
#close-signature {
cursor: pointer;
align-items: center;
margin-left: auto;
margin-right: 2%;
border-radius: 50%;
background-color: var(--primary-color);
color: white;
border: none;
padding: -3%;
margin-top: -5%;
font-size: 1em;
font-weight: bold;
}
#close-signature:hover {
background-color: var(--secondary-color);
color: var(--primary-color);
}
/* REQUEST MODAL */
.request-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.modal-content {
background-color: var(--secondary-color);
padding: 20px;
border-radius: 8px;
position: relative;
min-width: 300px;
}
.close-modal {
position: absolute;
top: 10px;
right: 10px;
border: none;
background: none;
font-size: 1.5em;
cursor: pointer;
font-weight: bold;
}
.close-modal:hover {
color: var(--accent-color);
}
.modal-members {
display: flex;
justify-content: space-between;
}
.modal-members ul li{
list-style: none;
}
.file-upload-container {
margin: 10px 0;
}
.file-list {
margin-top: 10px;
}
.file-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 5px;
margin: 5px 0;
background: var(--background-color-secondary);
border-radius: 4px;
}
.remove-file {
background: none;
border: none;
color: var(--text-color);
cursor: pointer;
padding: 0 5px;
}
.remove-file:hover {
color: var(--error-color);
}
#message-input {
width: 100%;
height: 50px;
resize: none;
padding: 10px;
box-sizing: border-box;
overflow: auto;
max-width: 100%;
border-radius: 10px;
}
/* Responsive */
@media screen and (max-width: 768px) {
.group-list {
display: none;
/* Masquer la liste des groupes sur les petits écrans */
}
.chat-area {
margin: 0;
}
}
::-webkit-scrollbar {
width: 5px;
height: 5px;
}
::-webkit-scrollbar-track {
background: var(--primary-color);
border-radius: 5px;
}
::-webkit-scrollbar-thumb {
background: var(--secondary-color);
border-radius: 5px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--accent-color);
}

1664
ihm/style/signature.css Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,2 @@
export declare function unpair(): Promise<void>;
export declare function initHeader(): Promise<void>;

View File

@ -0,0 +1 @@
export declare function closeConfirmationModal(): Promise<void>;

View File

@ -0,0 +1,10 @@
export default class QrScannerComponent extends HTMLElement {
videoElement: any;
wrapper: any;
qrScanner: any;
constructor();
connectedCallback(): void;
initializeScanner(): Promise<void>;
onQrCodeScanned(result: any): Promise<void>;
disconnectedCallback(): void;
}

View File

@ -0,0 +1 @@
export declare function initValidationModal(processDiffs: any): Promise<void>;

View File

@ -0,0 +1,14 @@
export interface ValidationRule {
quorum: number;
fields: string[];
min_sig_member: number;
}
/**
* Loads and injects the modal HTML into the document if not already loaded.
*/
export declare function loadValidationRuleModal(templatePath?: string): Promise<void>;
/**
* Opens the modal and lets the user input a ValidationRule.
* Calls the callback with the constructed rule on submit.
*/
export declare function showValidationRuleModal(onSubmit: (rule: ValidationRule) => void): void;

3
ihm/types/index.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
export { default as Services } from './services/service';
export { default as Database } from './services/database.service';
export { MessageType } from './models/process.model';

24
ihm/types/interface/groupInterface.d.ts vendored Normal file
View File

@ -0,0 +1,24 @@
import { DocumentSignature } from '~/models/signature.models';
export interface Group {
id: number;
name: string;
description: string;
roles: Array<{
name: string;
members: Array<{
id: string | number;
name: string;
}>;
documents?: Array<any>;
}>;
commonDocuments: Array<{
id: number;
name: string;
visibility: string;
description: string;
createdAt?: string | null;
deadline?: string | null;
signatures?: DocumentSignature[];
status?: string;
}>;
}

View File

@ -0,0 +1,10 @@
export interface Member {
id: string | number;
name: string;
email?: string;
avatar?: string;
processRoles?: Array<{
processId: number | string;
role: string;
}>;
}

9
ihm/types/main.d.ts vendored Normal file
View File

@ -0,0 +1,9 @@
import { AccountComponent } from './pages/account/account-component';
import { AccountElement } from './pages/account/account';
export { AccountComponent, AccountElement };
declare global {
interface HTMLElementTagNameMap {
'account-component': AccountComponent;
'account-element': AccountElement;
}
}

View File

@ -0,0 +1,118 @@
export declare const ALLOWED_ROLES: string[];
export declare const STORAGE_KEYS: {
pairing: string;
wallet: string;
process: string;
data: string;
};
export declare const defaultRows: {
column1: string;
column2: string;
column3: string;
}[];
export declare const mockNotifications: {
[key: string]: Notification[];
};
export declare const notificationMessages: string[];
export declare const mockDataRows: {
column1: string;
column2: string;
column3: string;
column4: string;
column5: string;
column6: string;
processName: string;
zone: string;
}[];
export declare const mockProcessRows: ({
process: string;
role: string;
notification: {
messages: {
id: number;
read: boolean;
date: string;
message: string;
}[];
unread?: undefined;
total?: undefined;
};
} | {
process: string;
role: string;
notification: {
unread: number;
total: number;
messages: {
id: number;
read: boolean;
date: string;
message: string;
}[];
};
})[];
export declare const mockContracts: {
'Contract #123': {
title: string;
date: string;
parties: string[];
terms: string[];
content: string;
};
'Contract #456': {
title: string;
date: string;
parties: string[];
terms: string[];
content: string;
};
'Contract #789': {
title: string;
date: string;
parties: string[];
terms: string[];
content: string;
};
'Contract #101': {
title: string;
date: string;
parties: string[];
terms: string[];
content: string;
};
'Contract #102': {
title: string;
date: string;
parties: string[];
terms: string[];
content: string;
};
'Contract #103': {
title: string;
date: string;
parties: string[];
terms: string[];
content: string;
};
'Contract #104': {
title: string;
date: string;
parties: string[];
terms: string[];
content: string;
};
'Contract #105': {
title: string;
date: string;
parties: string[];
terms: string[];
content: string;
};
'Contract #106': {
title: string;
date: string;
parties: string[];
terms: string[];
content: string;
};
};

View File

@ -0,0 +1,38 @@
export interface Row {
column1: string;
column2: string;
column3: string;
}
export interface Contract {
title: string;
date: string;
parties: string[];
terms: string[];
content: string;
}
export interface WalletRow {
column1: string;
column2: string;
column3: string;
}
export interface DataRow {
column1: string;
column2: string;
column3: string;
column4: string;
column5: string;
column6: string;
processName: string;
zone: string;
}
export interface Notification {
message: string;
timestamp: string;
isRead: boolean;
}
export interface NotificationMessage {
id: number;
read: boolean;
date: string;
message: string;
}

View File

@ -0,0 +1,9 @@
export declare const messagesMock: {
memberId: number;
messages: {
id: number;
sender: string;
text: string;
time: string;
}[];
}[];

6
ihm/types/models/backup.model.d.ts vendored Normal file
View File

@ -0,0 +1,6 @@
import { Device, Process, SecretsStore } from 'pkg/sdk_client';
export interface BackUp {
device: Device;
secrets: SecretsStore;
processes: Record<string, Process>;
}

View File

@ -0,0 +1,24 @@
export interface INotification {
id: number;
title: string;
description: string;
sendToNotificationPage?: boolean;
path?: string;
}
export interface IUser {
id: string;
information?: any;
}
export interface IMessage {
id: string;
message: any;
}
export interface UserDiff {
new_state_merkle_root: string;
field: string;
previous_value: string;
new_value: string;
notify_user: boolean;
need_validation: boolean;
proof: any;
}

56
ihm/types/models/process.model.d.ts vendored Normal file
View File

@ -0,0 +1,56 @@
export interface IProcess {
id: number;
name: string;
description: string;
icon?: string;
zoneList: IZone[];
}
export interface IZone {
id: number;
name: string;
path: string;
icon?: string;
}
export interface INotification {
id: number;
title: string;
description: string;
sendToNotificationPage?: boolean;
path?: string;
}
export declare enum MessageType {
LISTENING = "LISTENING",
REQUEST_LINK = "REQUEST_LINK",
LINK_ACCEPTED = "LINK_ACCEPTED",
CREATE_PAIRING = "CREATE_PAIRING",
PAIRING_CREATED = "PAIRING_CREATED",
ERROR = "ERROR",
VALIDATE_TOKEN = "VALIDATE_TOKEN",
RENEW_TOKEN = "RENEW_TOKEN",
GET_PAIRING_ID = "GET_PAIRING_ID",
GET_PROCESSES = "GET_PROCESSES",
GET_MY_PROCESSES = "GET_MY_PROCESSES",
PROCESSES_RETRIEVED = "PROCESSES_RETRIEVED",
RETRIEVE_DATA = "RETRIEVE_DATA",
DATA_RETRIEVED = "DATA_RETRIEVED",
DECODE_PUBLIC_DATA = "DECODE_PUBLIC_DATA",
PUBLIC_DATA_DECODED = "PUBLIC_DATA_DECODED",
GET_MEMBER_ADDRESSES = "GET_MEMBER_ADDRESSES",
MEMBER_ADDRESSES_RETRIEVED = "MEMBER_ADDRESSES_RETRIEVED",
CREATE_PROCESS = "CREATE_PROCESS",
PROCESS_CREATED = "PROCESS_CREATED",
UPDATE_PROCESS = "UPDATE_PROCESS",
PROCESS_UPDATED = "PROCESS_UPDATED",
NOTIFY_UPDATE = "NOTIFY_UPDATE",
UPDATE_NOTIFIED = "UPDATE_NOTIFIED",
VALIDATE_STATE = "VALIDATE_STATE",
STATE_VALIDATED = "STATE_VALIDATED",
HASH_VALUE = "HASH_VALUE",
VALUE_HASHED = "VALUE_HASHED",
GET_MERKLE_PROOF = "GET_MERKLE_PROOF",
MERKLE_PROOF_RETRIEVED = "MERKLE_PROOF_RETRIEVED",
VALIDATE_MERKLE_PROOF = "VALIDATE_MERKLE_PROOF",
MERKLE_PROOF_VALIDATED = "MERKLE_PROOF_VALIDATED",
ADD_DEVICE = "ADD_DEVICE",
DEVICE_ADDED = "DEVICE_ADDED"
}

60
ihm/types/models/signature.models.d.ts vendored Normal file
View File

@ -0,0 +1,60 @@
export interface Group {
id: number;
name: string;
description?: string;
roles: {
id?: number;
name: string;
members: {
id: string | number;
name: string;
}[];
documents?: {
id: number;
name: string;
description?: string;
visibility: string;
createdAt: string | null;
deadline: string | null;
signatures: DocumentSignature[];
status?: string;
files?: Array<{
name: string;
url: string;
}>;
}[];
}[];
}
export interface Message {
id: number;
sender: string;
text?: string;
time: string;
type: 'text' | 'file';
fileName?: string;
fileData?: string;
}
export interface MemberMessages {
memberId: string;
messages: Message[];
}
export interface DocumentSignature {
signed: boolean;
member: {
name: string;
};
signedAt?: string;
}
export interface RequestParams {
processId: number;
processName: string;
roleId: number;
roleName: string;
documentId: number;
documentName: string;
}
export interface Notification {
memberId: string;
text: string;
time: string;
}

View File

@ -0,0 +1,12 @@
import { AccountElement } from './account';
declare class AccountComponent extends HTMLElement {
_callback: any;
accountElement: AccountElement | null;
constructor();
connectedCallback(): void;
fetchData(): Promise<void>;
set callback(fn: any);
get callback(): any;
render(): void;
}
export { AccountComponent };

98
ihm/types/pages/account/account.d.ts vendored Normal file
View File

@ -0,0 +1,98 @@
declare global {
interface Window {
initAccount: () => void;
showContractPopup: (contractId: string) => void;
showPairing: () => Promise<void>;
showWallet: () => void;
showData: () => void;
addWalletRow: () => void;
confirmWalletRow: () => void;
cancelWalletRow: () => void;
openAvatarPopup: () => void;
closeAvatarPopup: () => void;
editDeviceName: (cell: HTMLTableCellElement) => void;
showNotifications: (processName: string) => void;
closeNotificationPopup: (event: Event) => void;
markAsRead: (processName: string, messageId: number, element: HTMLElement) => void;
exportRecovery: () => void;
confirmDeleteAccount: () => void;
deleteAccount: () => void;
updateNavbarBanner: (bannerUrl: string) => void;
saveBannerToLocalStorage: (bannerUrl: string) => void;
loadSavedBanner: () => void;
cancelAddRowPairing: () => void;
saveName: (cell: HTMLElement, input: HTMLInputElement) => void;
showProcessNotifications: (processName: string) => void;
handleLogout: () => void;
initializeEventListeners: () => void;
showProcess: () => void;
showProcessCreation: () => void;
showDocumentValidation: () => void;
updateNavbarName: (name: string) => void;
updateNavbarLastName: (lastName: string) => void;
showAlert: (title: string, text?: string, icon?: string) => void;
addRowPairing: () => void;
confirmRowPairing: () => void;
cancelRowPairing: () => void;
deleteRowPairing: (button: HTMLButtonElement) => void;
generateRecoveryWords: () => string[];
exportUserData: () => void;
updateActionButtons: () => void;
showQRCodeModal: (pairingId: string) => void;
}
}
declare class AccountElement extends HTMLElement {
private dom;
constructor();
connectedCallback(): void;
private showAlert;
private confirmDeleteAccount;
private deleteAccount;
private updateNavbarBanner;
private saveBannerToLocalStorage;
private loadSavedBanner;
private closeNotificationPopup;
private markAsRead;
private calculateNotifications;
private exportRecovery;
private generateRecoveryWords;
private exportUserData;
private updateActionButtons;
private getConfirmFunction;
private getCancelFunction;
private addRowPairing;
private updateTableContent;
private confirmRowPairing;
private cancelRowPairing;
private resetButtonContainer;
private deleteRowPairing;
private editDeviceName;
private finishEditing;
private handleAvatarUpload;
private showProcessCreation;
private showDocumentValidation;
private showProcess;
private showProcessNotifications;
private handleLogout;
private showContractPopup;
private hideAllContent;
private showPairing;
private showWallet;
private updateWalletTableContent;
private showData;
private addWalletRow;
private confirmWalletRow;
private cancelWalletRow;
private updateDataTableContent;
private openAvatarPopup;
private setupEventListeners;
private closeAvatarPopup;
private loadAvatar;
private loadUserInfo;
private updateNavbarName;
private updateNavbarLastName;
private updateProfilePreview;
private initializeEventListeners;
private showQRCodeModal;
}
export { AccountElement };

View File

@ -0,0 +1,33 @@
export interface Vin {
txid: string;
vout: number;
prevout: {
scriptpubkey: string;
scriptpubkey_asm: string;
scriptpubkey_type: string;
scriptpubkey_address: string;
value: number;
};
scriptsig: string;
scriptsig_asm: string;
witness: string[];
is_coinbase: boolean;
sequence: number;
}
export interface TransactionInfo {
txid: string;
version: number;
locktime: number;
vin: Vin[];
vout: any[];
size: number;
weight: number;
fee: number;
status: {
confirmed: boolean;
block_height: number;
block_hash: string;
block_time: number;
};
}
export declare function getDocumentValidation(container: HTMLElement): void;

View File

@ -0,0 +1,8 @@
import type { RoleDefinition } from '../../../pkg/sdk_client';
export declare function createKeyValueSection(title: string, id: string, isRoleSection?: boolean): {
element: HTMLDivElement;
getData: () => Record<string, RoleDefinition> | Record<string, string | {
type: string;
data: Uint8Array;
}>;
};

View File

@ -0,0 +1 @@
export declare function getProcessCreation(container: HTMLElement): Promise<void>;

4
ihm/types/pages/account/process.d.ts vendored Normal file
View File

@ -0,0 +1,4 @@
export declare function createProcessTab(container: HTMLElement, processes: {
name: string;
publicData: Record<string, any>;
}[]): HTMLElement;

View File

@ -0,0 +1,8 @@
export declare class LoginComponent extends HTMLElement {
_callback: any;
constructor();
connectedCallback(): void;
set callback(fn: any);
get callback(): any;
render(): void;
}

4
ihm/types/pages/home/home.d.ts vendored Normal file
View File

@ -0,0 +1,4 @@
import QrScannerComponent from '../../components/qrcode-scanner/qrcode-scanner-component';
export { QrScannerComponent };
export declare function initHomePage(): Promise<void>;
export declare function openModal(myAddress: string, receiverAddress: string): Promise<void>;

5
ihm/types/router.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
import '../public/style/4nk.css';
export declare let currentRoute: string;
export declare function navigate(path: string): Promise<void>;
export declare function init(): Promise<void>;
export declare function registerAllListeners(): Promise<void>;

View File

@ -0,0 +1,43 @@
export declare class Database {
private static instance;
private db;
private dbName;
private dbVersion;
private serviceWorkerRegistration;
private messageChannel;
private messageChannelForGet;
private serviceWorkerCheckIntervalId;
private storeDefinitions;
private constructor();
static getInstance(): Promise<Database>;
private init;
getDb(): Promise<IDBDatabase>;
getStoreList(): {
[key: string]: string;
};
registerServiceWorker(path: string): Promise<void>;
private waitForServiceWorkerActivation;
private checkForUpdates;
private handleServiceWorkerMessage;
private handleDownloadList;
private handleAddObjectResponse;
private handleGetObjectResponse;
addObject(payload: {
storeName: string;
object: any;
key: any;
}): Promise<void>;
batchWriting(payload: {
storeName: string;
objects: {
key: any;
object: any;
}[];
}): Promise<void>;
getObject(storeName: string, key: string): Promise<any | null>;
dumpStore(storeName: string): Promise<Record<string, any>>;
deleteObject(storeName: string, key: string): Promise<void>;
clearStore(storeName: string): Promise<void>;
requestStoreByIndex(storeName: string, indexName: string, request: string): Promise<any[]>;
}
export default Database;

28
ihm/types/services/modal.service.d.ts vendored Normal file
View File

@ -0,0 +1,28 @@
import type { RoleDefinition } from 'pkg/sdk_client';
interface ConfirmationModalOptions {
title: string;
content: string;
confirmText?: string;
cancelText?: string;
}
export default class ModalService {
private static instance;
private stateId;
private processId;
private constructor();
private paired_addresses;
private modal;
static getInstance(): Promise<ModalService>;
openLoginModal(myAddress: string, receiverAddress: string): void;
injectModal(members: any[]): Promise<void>;
injectCreationModal(members: any[]): Promise<void>;
injectWaitingModal(): Promise<void>;
injectValidationModal(processDiff: any): Promise<void>;
closeValidationModal(): Promise<void>;
openPairingConfirmationModal(roleDefinition: Record<string, RoleDefinition>, processId: string, stateId: string): Promise<void>;
confirmLogin(): void;
closeLoginModal(): Promise<void>;
showConfirmationModal(options: ConfirmationModalOptions, fullscreen?: boolean): Promise<boolean>;
closeConfirmationModal(): Promise<void>;
}
export {};

167
ihm/types/services/service.d.ts vendored Normal file
View File

@ -0,0 +1,167 @@
import type { ApiReturn, Device, Member, MerkleProofResult, Process, ProcessState, RoleDefinition, SecretsStore, UserDiff } from '../../pkg/sdk_client';
import { BackUp } from '~/models/backup.model';
export declare const U32_MAX = 4294967295;
export default class Services {
private static initializing;
private static instance;
private processId;
private stateId;
private sdkClient;
private processesCache;
private myProcesses;
private notifications;
private subscriptions;
private database;
private routingInstance;
private relayAddresses;
private membersList;
private currentBlockHeight;
private constructor();
static getInstance(): Promise<Services>;
init(): Promise<void>;
setProcessId(processId: string | null): void;
setStateId(stateId: string | null): void;
getProcessId(): string | null;
getStateId(): string | null;
/**
* Calls `this.addWebsocketConnection` for each `wsurl` in relayAddresses.
* Waits for at least one handshake message before returning.
*/
connectAllRelays(): Promise<void>;
addWebsocketConnection(url: string): Promise<void>;
/**
* Add or update a key/value pair in relayAddresses.
* @param wsurl - The WebSocket URL (key).
* @param spAddress - The SP Address (value).
*/
updateRelay(wsurl: string, spAddress: string): void;
/**
* Retrieve the spAddress for a given wsurl.
* @param wsurl - The WebSocket URL to look up.
* @returns The SP Address if found, or undefined if not.
*/
getSpAddress(wsurl: string): string | undefined;
/**
* Get all key/value pairs from relayAddresses.
* @returns An array of objects containing wsurl and spAddress.
*/
getAllRelays(): {
wsurl: string;
spAddress: string;
}[];
/**
* Print all key/value pairs for debugging.
*/
printAllRelays(): void;
isPaired(): boolean;
unpairDevice(): Promise<void>;
getSecretForAddress(address: string): Promise<string | null>;
getAllSecrets(): Promise<SecretsStore>;
getAllDiffs(): Promise<Record<string, UserDiff>>;
getDiffByValue(value: string): Promise<UserDiff | null>;
private getTokensFromFaucet;
checkConnections(members: Member[]): Promise<void>;
connectAddresses(addresses: string[]): Promise<ApiReturn>;
private ensureSufficientAmount;
private waitForAmount;
createPairingProcess(userName: string, pairWith: string[]): Promise<ApiReturn>;
private isFileBlob;
private splitData;
createProcess(privateData: Record<string, any>, publicData: Record<string, any>, roles: Record<string, RoleDefinition>): Promise<ApiReturn>;
updateProcess(process: Process, privateData: Record<string, any>, publicData: Record<string, any>, roles: Record<string, RoleDefinition> | null): Promise<ApiReturn>;
createPrdUpdate(processId: string, stateId: string): Promise<ApiReturn>;
createPrdResponse(processId: string, stateId: string): Promise<ApiReturn>;
approveChange(processId: string, stateId: string): Promise<ApiReturn>;
rejectChange(processId: string, stateId: string): Promise<ApiReturn>;
resetDevice(): Promise<void>;
sendNewTxMessage(message: string): void;
sendCommitMessage(message: string): void;
sendCipherMessages(ciphers: string[]): void;
sendFaucetMessage(message: string): void;
parseCipher(message: string): Promise<void>;
parseNewTx(newTxMsg: string): Promise<void>;
handleApiReturn(apiReturn: ApiReturn): Promise<void>;
openPairingConfirmationModal(processId: string): Promise<void>;
confirmPairing(): Promise<void>;
updateDevice(): Promise<void>;
pairDevice(): Promise<void>;
getAmount(): BigInt;
getDeviceAddress(): string;
dumpDeviceFromMemory(): Device;
dumpNeuteredDevice(): Device | null;
getPairingProcessId(): string;
saveDeviceInDatabase(device: Device): Promise<void>;
getDeviceFromDatabase(): Promise<Device | null>;
getMemberFromDevice(): Promise<string[] | null>;
isChildRole(parent: any, child: any): boolean;
rolesContainsUs(roles: Record<string, RoleDefinition>): boolean;
rolesContainsMember(roles: Record<string, RoleDefinition>, pairingProcessId: string): boolean;
dumpWallet(): Promise<any>;
createFaucetMessage(): any;
createNewDevice(): Promise<string>;
restoreDevice(device: Device): void;
updateDeviceBlockHeight(): Promise<void>;
private removeProcess;
batchSaveProcessesToDb(processes: Record<string, Process>): Promise<void>;
saveProcessToDb(processId: string, process: Process): Promise<void>;
saveBlobToDb(hash: string, data: Blob): Promise<void>;
getBlobFromDb(hash: string): Promise<Blob | null>;
saveDataToStorage(hash: string, data: Blob, ttl: number | null): Promise<void>;
fetchValueFromStorage(hash: string): Promise<any | null>;
testDataInStorage(hash: string): Promise<Record<string, boolean | null> | null>;
saveDiffsToDb(diffs: UserDiff[]): Promise<void>;
getProcess(processId: string): Promise<Process | null>;
getProcesses(): Promise<Record<string, Process>>;
restoreProcessesFromBackUp(processes: Record<string, Process>): Promise<void>;
restoreProcessesFromDB(): Promise<void>;
clearSecretsFromDB(): Promise<void>;
restoreSecretsFromBackUp(secretsStore: SecretsStore): Promise<void>;
restoreSecretsFromDB(): Promise<void>;
decodeValue(value: number[]): any | null;
decryptAttribute(processId: string, state: ProcessState, attribute: string): Promise<any | null>;
getNotifications(): any[] | null;
setNotifications(notifications: any[]): void;
importJSON(backup: BackUp): Promise<void>;
createBackUp(): Promise<BackUp | null>;
device1: boolean;
device2Ready: boolean;
resetState(): void;
handleHandshakeMsg(url: string, parsedMsg: any): Promise<void>;
private lookForStateId;
/**
* Waits for at least one handshake message to be received from any connected relay.
* This ensures that the relay addresses are fully populated and the member list is updated.
* @returns A promise that resolves when at least one handshake message is received.
*/
private waitForHandshakeMessage;
/**
* Retourne la liste de tous les membres ordonnés par leur process id
* @returns Un tableau contenant tous les membres
*/
getAllMembersSorted(): Record<string, Member>;
getAllMembers(): Record<string, Member>;
getAddressesForMemberId(memberId: string): string[] | null;
compareMembers(memberA: string[], memberB: string[]): boolean;
handleCommitError(response: string): Promise<void>;
getRoles(process: Process): Record<string, RoleDefinition> | null;
getPublicData(process: Process): Record<string, any> | null;
getProcessName(process: Process): string | null;
getMyProcesses(): Promise<string[] | null>;
requestDataFromPeers(processId: string, stateIds: string[], roles: Record<string, RoleDefinition>[]): Promise<void>;
hexToBlob(hexString: string): Blob;
hexToUInt8Array(hexString: string): Uint8Array;
blobToHex(blob: Blob): Promise<string>;
getHashForFile(commitedIn: string, label: string, fileBlob: {
type: string;
data: Uint8Array;
}): string;
getMerkleProofForFile(processState: ProcessState, attributeName: string): MerkleProofResult;
validateMerkleProof(proof: MerkleProofResult, hash: string): boolean;
getLastCommitedState(process: Process): ProcessState | null;
getLastCommitedStateIndex(process: Process): number | null;
getUncommitedStates(process: Process): ProcessState[];
getStateFromId(process: Process, stateId: string): ProcessState | null;
getNextStateAfterId(process: Process, stateId: string): ProcessState | null;
isPairingProcess(roles: Record<string, RoleDefinition>): boolean;
updateMemberPublicName(process: Process, newName: string): Promise<ApiReturn>;
}

View File

@ -0,0 +1,4 @@
import { AxiosResponse } from 'axios';
export declare function storeData(servers: string[], key: string, value: Blob, ttl: number | null): Promise<AxiosResponse | null>;
export declare function retrieveData(servers: string[], key: string): Promise<ArrayBuffer | null>;
export declare function testData(servers: string[], key: string): Promise<Record<string, boolean | null> | null>;

17
ihm/types/services/token.d.ts vendored Normal file
View File

@ -0,0 +1,17 @@
interface TokenPair {
accessToken: string;
refreshToken: string;
}
export default class TokenService {
private static instance;
private readonly SECRET_KEY;
private readonly ACCESS_TOKEN_EXPIRATION;
private readonly REFRESH_TOKEN_EXPIRATION;
private readonly encoder;
private constructor();
static getInstance(): Promise<TokenService>;
generateSessionToken(origin: string): Promise<TokenPair>;
validateToken(token: string, origin: string): Promise<boolean>;
refreshAccessToken(refreshToken: string, origin: string): Promise<string | null>;
}
export {};

1
ihm/types/utils/document.utils.d.ts vendored Normal file
View File

@ -0,0 +1 @@
export declare function getCorrectDOM(componentTag: string): Node;

4
ihm/types/utils/html.utils.d.ts vendored Normal file
View File

@ -0,0 +1,4 @@
export declare function interpolate(template: string, data: {
[key: string]: string;
}): string;
export declare function getCorrectDOM(componentTag: string): Node;

12
ihm/types/utils/messageMock.d.ts vendored Normal file
View File

@ -0,0 +1,12 @@
declare class MessageStore {
private readonly STORAGE_KEY;
private messages;
constructor();
private loadFromLocalStorage;
getMessages(): any[];
setMessages(messages: any[]): void;
private saveToLocalStorage;
addMessage(memberId: string | number, message: any): void;
}
export declare const messageStore: MessageStore;
export {};

23
ihm/types/utils/notification.store.d.ts vendored Normal file
View File

@ -0,0 +1,23 @@
interface INotification {
id: number;
title: string;
description: string;
time?: string;
memberId?: string;
}
declare class NotificationStore {
private static instance;
private notifications;
private constructor();
static getInstance(): NotificationStore;
addNotification(notification: INotification): void;
removeNotification(index: number): void;
getNotifications(): INotification[];
private saveToLocalStorage;
private loadFromLocalStorage;
private updateUI;
private renderNotificationBoard;
refreshNotifications(): void;
}
export declare const notificationStore: NotificationStore;
export {};

5
ihm/types/utils/service.utils.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
export declare function splitPrivateData(data: Record<string, any>, privateFields: string[]): {
privateData: Record<string, any>;
publicData: Record<string, any>;
};
export declare function isValid32ByteHex(value: string): boolean;

8
ihm/types/utils/sp-address.utils.d.ts vendored Normal file
View File

@ -0,0 +1,8 @@
export declare function copyToClipboard(fullAddress: string): Promise<void>;
export declare function generateEmojiList(): string[];
export declare function addressToEmoji(text: string): Promise<string>;
export declare function displayEmojis(text: string): Promise<void>;
export declare function initAddressInput(): void;
export declare function prepareAndSendPairingTx(): Promise<void>;
export declare function generateQRCode(spAddress: string): Promise<void>;
export declare function generateCreateBtn(): Promise<void>;

View File

@ -0,0 +1,2 @@
export declare function cleanSubscriptions(): void;
export declare function addSubscription(element: Element | Document, event: any, eventHandler: EventListenerOrEventListenerObject): void;

5
ihm/types/websockets.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
import type { AnkFlag } from 'pkg/sdk_client';
export declare function initWebsocket(url: string): Promise<void>;
export declare function sendMessage(flag: AnkFlag, message: string): void;
export declare function getUrl(): string;
export declare function close(): void;

15
jest.config.js Normal file
View File

@ -0,0 +1,15 @@
/** @type {import('jest').Config} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'jsdom',
roots: ['<rootDir>/tests'],
moduleNameMapper: {
'^@/components/(.*)$': '<rootDir>/src/components/$1',
'^@/screens/(.*)$': '<rootDir>/src/screens/$1',
'^@/store/(.*)$': '<rootDir>/src/store/$1',
'^@/store$': '<rootDir>/src/store/index.ts',
'^@/services/(.*)$': '<rootDir>/src/services/$1',
'^@/bridge/(.*)$': '<rootDir>/src/bridge/$1'
},
setupFilesAfterEnv: ['<rootDir>/tests/setup.ts']
};

10746
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

40
package.json Normal file
View File

@ -0,0 +1,40 @@
{
"name": "sdk_wallet",
"version": "0.1.0",
"private": true,
"description": "Wallet Bitcoin mobile 4NK: conteneur React Native + WebView (ihm_client) + intégration sdk_client.",
"license": "MIT",
"scripts": {
"postinstall": "npm run prepare",
"prepare": "node -e \"console.log('prepare sdk_wallet')\"",
"build:ihm": "pwsh -File ./scripts/build-ihm.ps1",
"copy:ihm": "pwsh -File ./scripts/copy-ihm-dist.ps1",
"sync:ihm": "npm run build:ihm && npm run copy:ihm",
"typecheck": "tsc -p .",
"test": "jest --runInBand",
"lint": "echo 'lint placeholder'",
"build": "npm run typecheck",
"dev": "node -e \"console.log('Utilisez le dossier web/ pour l'hôte navigateur: npm run serve:web')\"",
"serve:web": "http-server ./web -p 5174 -c-1"
},
"dependencies": {
"@reduxjs/toolkit": "^2.2.5",
"react": "18.2.0",
"react-native": "^0.74.7",
"react-native-safe-area-context": "^4.10.5",
"react-native-webview": "^13.8.6",
"react-redux": "^9.1.2",
"uuid": "^9.0.1"
},
"devDependencies": {
"@types/jest": "^29.5.14",
"@types/react": "^18.3.3",
"@types/react-native": "^0.73.0",
"@types/uuid": "^9.0.8",
"jest": "^29.7.0",
"jest-environment-jsdom": "^30.1.0",
"http-server": "^14.1.1",
"ts-jest": "^29.2.5",
"typescript": "^5.5.4"
}
}

14
scripts/build-ihm.ps1 Normal file
View File

@ -0,0 +1,14 @@
param()
$ErrorActionPreference = 'Stop'
Push-Location "$PSScriptRoot/../..\ihm_client"
try {
if (Test-Path package-lock.json) {
npm ci --silent
} else {
npm install --silent
}
npm run build
} finally {
Pop-Location
}

View File

@ -0,0 +1,20 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd)"
cd "$ROOT_DIR"
version_file="VERSION"
[[ -f TEMPLATE_VERSION ]] && version_file="TEMPLATE_VERSION"
[[ -f "$version_file" ]] || { echo "Version file missing ($version_file)"; exit 1; }
v=$(tr -d '\r' < "$version_file" | head -n1)
[[ -n "$v" ]] || { echo "Empty version"; exit 1; }
echo "Version file: $version_file=$v"
if ! grep -Eq "^## \\[$(echo "$v" | sed 's/^v//')\\]" CHANGELOG.md; then
echo "CHANGELOG entry for $v not found"; exit 1;
fi
echo "Version alignment OK"

Some files were not shown because too many files have changed in this diff Show More