node-red-contrib-step-escape 1.0.42

A custom Node-RED Dashboard node that displays a block with a dark background, grey right border, full tab height, and 35% width.

npm install node-red-contrib-step-escape

🧩 Node-RED Step Escape

Step Escape est une node personnalisée pour Node-RED conçue spécialement pour les escape games. Elle permet de suivre l'avancée des énigmes en temps réel avec une interface claire et interactive.


📸 Aperçu

Interface dans le dashboard :

Aperçu Dashboard

Exemple de configuration de la node :

Configuration Node


⚙️ Fonctionnement

  • Chaque étape possède un nom et une valeur de validation. Configuration Node
  • Lorsqu'un msg.payload correspond à une étape, celle-ci est validée (en vert).
  • La suivante passe en attente (orange). Configuration Node
  • Si une étape est validée hors ordre, elle passe en ordre invalide (bleu). Configuration Node

🔁 Réinitialisation

Envoyer msg.payload = "reset" pour réinitialiser toutes les étapes.


🆔 Champ ID

Le champ ID est pour l’instant inutilisé. Il peut rester vide.


🧩 Prérequis

  • Node-RED >= 3.x
  • node-red-dashboard
  • Une ui_template personnalisée à connecter pour l’affichage

🔌 Connexion

Connectez la node step-escape directement à une ui_template pour la transmission des étapes. Configuration Node


Code node ui-template (affichage)

Voici le code JSON de la node ui_template

[{"id":"f2e6cf9d10d52705","type":"ui_template","z":"f28c8fd26da314f6","group":"c3d4e5f6.a7b8c9","name":"Affichage UI","order":3,"width":0,"height":0,"format":"<div id=\"custom-block\" class=\"dark-theme\" style=\"\n    position: fixed;\n    left: 0;\n    top: 64px;\n    border-right: 2px solid #808080;\n    height: -webkit-fill-available;\n    width: 25vw;\n    box-sizing: border-box;\n    z-index: 1;\n\">\n    <div class=\"fade-top\"></div>\n\n    <button id=\"theme-toggle\">\n        <img id=\"theme-icon\" src=\"https://cine-production.github.io/ServiceTiers/BASEDONNEE/node-red/dark.svg\" alt=\"theme icon\" style=\"height: -webkit-fill-available;\">\n    </button>\n\n    <div id=\"steps-container\" style=\"padding: 10px;\">\n        <div ng-repeat=\"step in msg.payload track by $index\" class=\"step-wrapper\">\n            <div class=\"timeline\">\n                <div class=\"circle\" ng-class=\"{\n                         'line-top': $index > 0,\n                         'line-bottom': $index < msg.payload.length - 1,\n                         'validated': step.state === 'validated',\n                         'in-progress': step.state === 'in-progress',\n                         'not-executed': step.state === 'not-executed' || !step.state,\n                         'invalid-order': step.state === 'invalid-order'\n                     }\">\n                    <span class=\"circle-icon\" ng-if=\"step.state === 'validated'\">✔</span>\n                    <span class=\"circle-icon\" ng-if=\"step.state === 'invalid-order'\">!</span>\n                </div>\n            </div>\n            <div class=\"step-item\" ng-class=\"{\n                     'validated': step.state === 'validated',\n                     'in-progress': step.state === 'in-progress',\n                     'not-executed': step.state === 'not-executed' || !step.state,\n                     'invalid-order': step.state === 'invalid-order'\n                 }\">\n                {{step.name || 'Unnamed Step'}}\n            </div>\n        </div>\n        <div class=\"session-end-indicator\" ng-class=\"{'complete': isSessionComplete}\">\n            <hr class=\"session-line\">\n            <div class=\"session-text\">✔ Session terminée</div>\n        </div>\n\n    </div>\n    <div class=\"fade-bottom\"></div>\n</div>\n</div>\n\n<script>\n    (function(scope) {\n        // Handle theme\n        const block = document.getElementById(\"custom-block\");\n        const toggleBtn = document.getElementById(\"theme-toggle\");\n        const themeIcon = document.getElementById(\"theme-icon\");\n\n        const applyTheme = (theme) => {\n            console.log(\"UI: Applying theme\", theme);\n            if (theme === \"light\") {\n                block.classList.remove(\"dark-theme\");\n                block.classList.add(\"light-theme\");\n                themeIcon.src = \"https://cine-production.github.io/ServiceTiers/BASEDONNEE/node-red/light.svg\";\n            } else {\n                block.classList.remove(\"light-theme\");\n                block.classList.add(\"dark-theme\");\n                themeIcon.src = \"https://cine-production.github.io/ServiceTiers/BASEDONNEE/node-red/dark.svg\";\n            }\n        };\n\n        const savedTheme = localStorage.getItem(\"customBlockTheme\") || \"dark\";\n        applyTheme(savedTheme);\n\n        toggleBtn.addEventListener(\"click\", () => {\n            const newTheme = block.classList.contains(\"dark-theme\") ? \"light\" : \"dark\";\n            localStorage.setItem(\"customBlockTheme\", newTheme);\n            applyTheme(newTheme);\n        });\n\n        // Log received messages\n        scope.$watch('msg', function(msg) {\n            console.log(\"UI: Received message:\", msg);\n            if (msg && msg.topic === 'update_steps' && Array.isArray(msg.payload)) {\n                console.log(\"UI: Updating steps:\", JSON.stringify(msg.payload));\n            }\n        });\n        scope.$watch('msg.payload', function(steps) {\n    if (Array.isArray(steps)) {\n        scope.isSessionComplete = steps.every(step => step.state === 'validated');\n    } else {\n        scope.isSessionComplete = false;\n    }\n});\n    })(scope);\n</script>\n\n<style>\n    .session-text {\n        font-size: 16px;\n        font-weight: bold;\n        color: #888;\n        /* gris par défaut */\n        transition: color 0.3s ease;\n    }\n\n    .session-end-indicator.complete .session-text {\n        animation: all 2s ease-in-out;\n        color: #4caf50;\n        font-weight: bold;\n        transform: scale(1.2);\n    }\n\n    .session-end-indicator .session-line {\n        width: 80%;\n        border: none;\n        border-top: 2px solid #888;\n        /* gris par défaut */\n        margin-bottom: 10px;\n        transition: border-color 0.3s ease;\n    }\n\n    .session-end-indicator.complete .session-line {\n        border-color: #4caf50;\n        /* vert quand terminé */\n    }\n\n    .session-end-indicator {\n        display: flex;\n        flex-direction: column;\n        align-items: center;\n        margin-top: 60px;\n        margin-bottom: 30px;\n        text-align: center;\n    }\n\n    .session-line {\n        width: 80%;\n        border: none;\n        border-top: 2px solid #4caf50;\n        margin-bottom: 10px;\n    }\n\n\n\n    #theme-toggle {\n        position: absolute;\n        top: 10px;\n        right: 10px;\n        padding: 5px;\n        background-color: transparent;\n        border: none;\n        cursor: pointer;\n        border-radius: 5px;\n        width: 28px;\n        height: 28px;\n        display: flex;\n        align-items: center;\n        justify-content: center;\n        z-index: 2;\n    }\n\n    #custom-block.dark-theme {\n        background-color: #333;\n        color: white;\n    }\n\n    #custom-block.light-theme {\n        background-color: #f1f1f1;\n        color: black;\n    }\n\n    .step-item.light-theme {\n        background-color: #f1f1f1;\n        color: black;\n    }\n\n    #custom-block.light-theme #theme-toggle {\n        background-color: #d3d3d3;\n        color: #636363;\n    }\n\n    #custom-block.light-theme #theme-toggle img {\n        filter: brightness(0.4);\n    }\n\n    #custom-block.dark-theme #theme-toggle {\n        background-color: #616161;\n        color: #dddddd;\n    }\n\n    #custom-block.dark-theme #theme-toggle img {\n        filter: brightness(0.7);\n    }\n\n    .layout-row {\n        width: 75vw;\n        margin-left: auto;\n    }\n\n    .fade-top,\n    .fade-bottom {\n        position: absolute;\n        left: 0;\n        width: 100%;\n        height: 60px;\n        pointer-events: none;\n        z-index: 2;\n    }\n\n    .fade-top {\n        top: 0;\n        background: linear-gradient(to bottom, #333, transparent);\n    }\n\n    .fade-bottom {\n        bottom: 0;\n        background: linear-gradient(to top, #333, transparent);\n    }\n\n    #custom-block.light-theme .fade-top {\n        background: linear-gradient(to bottom, #f1f1f1, transparent);\n    }\n\n    #custom-block.light-theme .fade-bottom {\n        background: linear-gradient(to top, #f1f1f1, transparent);\n    }\n\n    #steps-container {\n        overflow: scroll;\n        scrollbar-width: none;\n        /* Firefox */\n        -ms-overflow-style: none;\n        /* IE/Edge */\n        height: -webkit-fill-available;\n    }\n\n    #steps-container::-webkit-scrollbar {\n        display: none;\n        /* Chrome, Safari, Edge */\n    }\n\n    .step-wrapper {\n        display: flex;\n        align-items: center;\n        gap: 35px;\n        padding: 0px 25px;\n        margin-bottom: 40px;\n    }\n\n    #steps-container>*:first-child {\n        margin-top: 90px;\n    }\n\n    #steps-container>*:last-child {\n        margin-bottom: 300px;\n    }\n\n    .timeline {\n        position: relative;\n        width: 24px;\n        display: flex;\n        justify-content: center;\n        align-items: center;\n        flex-shrink: 0;\n    }\n\n    .circle {\n        width: 24px;\n        height: 24px;\n        border-radius: 50%;\n        position: relative;\n        z-index: 1;\n        outline-style: solid;\n        outline-offset: 3px;\n        display: flex;\n        align-items: center;\n        justify-content: center;\n        transition: background-color 0.3s ease, outline-width 0.3s ease, transform 0.3s ease;\n    }\n\n    .step-item:hover {\n        transform: scale(1.05);\n    }\n\n    .circle-icon {\n        color: white;\n        font-size: 14px;\n        font-weight: bold;\n        transition: opacity 0.3s ease;\n    }\n\n    .circle.line-top::before,\n    .circle.line-bottom::after {\n        content: \"\";\n        position: absolute;\n        width: 2px;\n        background-color: gray;\n        left: 50%;\n        transform: translateX(-50%);\n        transition: background-color 0.3s ease;\n    }\n\n    .circle.line-top::before {\n        top: -39px;\n        height: 28px;\n    }\n\n    .circle.line-bottom::after {\n        bottom: -39px;\n        height: 28px;\n    }\n\n    .step-item {\n        flex: 1;\n        font-size: 16px;\n        padding: 18px;\n        border-radius: 7px;\n        height: 22px;\n        transition: background-color 0.3s ease, color 0.3s ease, transform 0.3s ease, font-weight 0.3s ease;\n    }\n\n    .circle.not-executed {\n        background-color: #535353 !important;\n        outline-color: #9b9b9b !important;\n        outline-width: 2px !important;\n    }\n\n    .step-item.not-executed {\n        background: #4d4d4d !important;\n        color: #888 !important;\n    }\n\n    .circle.in-progress {\n        background-color: #f28c38 !important;\n        outline-color: #ffffff !important;\n        outline-width: 2px !important;\n    }\n\n    .step-item.in-progress {\n        background: #5e5e5e !important;\n        color: #ffffff !important;\n        font-weight: bold !important;\n    }\n\n    .circle.validated {\n        background-color: #4caf50 !important;\n        outline-color: #ffffff !important;\n        outline-width: 1px !important;\n    }\n\n    .step-item.validated {\n        background: #666666 !important;\n        color: #ffffff !important;\n        font-weight: normal !important;\n    }\n\n    .circle.invalid-order {\n        background-color: #2196F3 !important;\n        outline-color: #ffffff !important;\n        outline-width: 1px !important;\n    }\n\n    .step-item.invalid-order {\n        background: #FFCC80 !important;\n        color: #333333 !important;\n        font-weight: normal !important;\n    }\n</style>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":1290,"y":500,"wires":[[]]},{"id":"c3d4e5f6.a7b8c9","type":"ui_group","name":"Default","tab":"d4e5f6a7.b8c9d0","order":1,"disp":true,"width":"6","collapse":false},{"id":"d4e5f6a7.b8c9d0","type":"ui_tab","name":"Home","icon":"dashboard","order":1,"disabled":false,"hidden":false}]

Node Info

Version: 1.0.42
Updated 1 day ago
License: MIT
Rating: 5.0 1

Categories

Actions

Rate:

Downloads

256 in the last week

Nodes

  • step-escape

Keywords

  • node-red
  • dashboard
  • ui
  • custom

Maintainers