Ui builder how to?

Hello,
I installed UI builder succesfully.
I got a simple flow but now i am stuck ? How to generated the ui builder ?


Check out some of the examples that are contained in the library. These are installed when you install uibuilder. Go to the menu, import, examples, uibuilder.

There are also a load of examples on the WIKI.

All you need to do from what you have is to add an inject node to provide input and a couple of debug nodes on the outputs. Then open the uibuilder node in the Editor and click on the "Open Page" button to show the ui. Now click on the inject and see the message appear in the front-end ui. Similarly try the example input form in the ui and check the outputs in Node-RED.

Once you have seen that and are beginning to understand the overall flow back and forth, you can try changing the front-end code to get a ui that does what you want it to. Connect up a more useful input flow to get data to the front-end.


Some more examples. Apologies if these don't directly work, some of them might require the next version of uibuilder, if so just ignore them or at least take a look at the general flow pictures.

Here is another caching example with a simple switch ui

[{"id":"c81e9ded.1f863","type":"uibuilder","z":"18cb249f.38bafb","g":"636c4f3f.9afb9","name":"","topic":"","url":"uiswitch","fwdInMessages":false,"allowScripts":false,"allowStyles":false,"copyIndex":true,"showfolder":false,"useSecurity":false,"sessionLength":"","tokenAutoExtend":false,"x":620,"y":1340,"wires":[["9f8d4d57.3157e","16aa3d7.761c5c3"],["ec4e6395.b7646"]]},{"id":"53ecf4d4.dfab4c","type":"inject","z":"18cb249f.38bafb","g":"636c4f3f.9afb9","name":"at_home: true","repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"name\":\"at_home\",\"service_name\":\"at_home\",\"characteristic\":\"On\",\"value\":true}","payloadType":"json","x":170,"y":1340,"wires":[["61f76ec.b2a469"]]},{"id":"8df7c15b.e3cc4","type":"inject","z":"18cb249f.38bafb","g":"636c4f3f.9afb9","name":"at_home: false","repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"name\":\"at_home\",\"service_name\":\"at_home\",\"characteristic\":\"On\",\"value\":false}","payloadType":"json","x":160,"y":1380,"wires":[["61f76ec.b2a469"]]},{"id":"b912e4ce.5a69e8","type":"link in","z":"18cb249f.38bafb","g":"636c4f3f.9afb9","name":"home-replay","links":["9f8d4d57.3157e","ec4e6395.b7646","6327f550.24f41c"],"x":215,"y":1300,"wires":[["61f76ec.b2a469"]]},{"id":"9f8d4d57.3157e","type":"link out","z":"18cb249f.38bafb","g":"636c4f3f.9afb9","name":"home-controls","links":["b912e4ce.5a69e8"],"x":755,"y":1300,"wires":[]},{"id":"ec4e6395.b7646","type":"link out","z":"18cb249f.38bafb","g":"636c4f3f.9afb9","name":"","links":["ae0fd0a1.2f9f7","b912e4ce.5a69e8"],"x":755,"y":1380,"wires":[]},{"id":"61f76ec.b2a469","type":"function","z":"18cb249f.38bafb","g":"636c4f3f.9afb9","name":"cache by payload.name","func":"/* jshint asi: true */\n\n// cache variable name - needs to be unique for each cache\nconst cacheVarName = 'ui_msgs'\n\n// saved context\nlet ui_msgs = context.get(cacheVarName) || {}\n\n// Handle cache control messages\nif (msg.hasOwnProperty('cacheControl')) {\n    // Replay cache if requested\n    if (msg.cacheControl === 'REPLAY') {\n        for (let name in ui_msgs) {\n            node.send({\n                \"topic\": name,\n                \"payload\": ui_msgs[name],\n                \"_socketId\": msg._socketId\n            })\n        }\n        return null\n    } else if (msg.cacheControl === 'CLEAR') {\n        // Or clear the cach (no msg sent)\n        context.set(cacheVarName, {})\n        return null\n    } else {\n        // or do nothing\n        return null\n    }\n}\n\n\n// ignore other uibuilder control messages\nif (msg.hasOwnProperty('uibuilderCtrl')) return null\n\nif (msg.hasOwnProperty('payload') && msg.payload.hasOwnProperty('name')) {\n    // Keep the last msg.payload by msg.payload.name\n    ui_msgs[msg.payload.name] = msg.payload\n    \n    // save context for next time\n    context.set(cacheVarName, ui_msgs)\n    \n    msg._socketId = null\n} else {\n    node.warn(\"no payload or payload.name\")\n    msg = null\n}\n\n// Show number of cached msgs in status\nnode.status({fill:'green',shape:'ring',text:'Cached: ' + Object.keys(ui_msgs).length})\n\nreturn msg;\n","outputs":1,"noerr":0,"x":430,"y":1340,"wires":[["c81e9ded.1f863"]]},{"id":"16aa3d7.761c5c3","type":"debug","z":"18cb249f.38bafb","g":"636c4f3f.9afb9","name":"uib output","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":800,"y":1340,"wires":[]},{"id":"affc4151.63336","type":"comment","z":"18cb249f.38bafb","g":"636c4f3f.9afb9","name":"index.html","info":"<!doctype html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    <meta name=\"viewport\" content=\"width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes\">\n\n    <title>Node-RED UI Builder</title>\n    <meta name=\"description\" content=\"Node-RED UI Builder - VueJS + bootstrap-vue version\">\n\n    <link rel=\"icon\" href=\"./images/node-blue.ico\">\n\n    <!-- See https://goo.gl/OOhYW5 -->\n    <link rel=\"manifest\" href=\"./manifest.json\">\n    <meta name=\"theme-color\" content=\"#3f51b5\">\n    <link type=\"text/css\" rel=\"stylesheet\" href=\"../uibuilder/vendor/bootstrap/dist/css/bootstrap.min.css\" />\n    <link type=\"text/css\" rel=\"stylesheet\" href=\"../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.css\" />\n    \n    <link rel=\"stylesheet\" href=\"./index.css\" media=\"all\">\n</head>\n<body>\n    <div id=\"app\" v-cloak>\n      <b-container id=\"app_container\">           \n        <b-card no-body>\n          <b-tabs card small>\n            <b-tab title=\"Main\" active>\n              <b-card header=\"Control\" border-variant=\"primary\">\n                <acc-switch name=\"at_home\" v-model=\"v_value.at_home\" badge></acc-switch>\n                <acc-switch name=\"security_system\" v-model=\"v_value.security_system\"></acc-switch>\n                <acc-switch name=\"heating\" v-model=\"v_value.heating\"></acc-switch>\n                <acc-switch name=\"greenhouse\" v-model=\"v_value.greenhouse\"></acc-switch>\n              </b-card>\n            </b-tab>\n\n            <b-tab title=\"Accessories\">\n              <b-card header=\"Accessories\" border-variant=\"primary\">\n                <b-card-text>an accessories list ...</b-card-text>\n              </b-card>\n            </b-tab>\n          </b-tabs>\n          \n          <b-card v-if=debug header=\"Debug\" border-variant=\"info\">\n            <b-card-text v-if=\"msg\">last msg: {{msg.payload}}</b-card-text>\n            <b-card-text>accessories: {{accessories}}</b-card-text>\n            <b-card-text>v_value: {{v_value}}</b-card-text>\n          </b-card>\n        </b-card>\n      </b-container>\n    </div>\n    \n    <script src=\"../uibuilder/vendor/socket.io/socket.io.js\"></script>\n\n    <!--  Vendor Libraries - Load in the right order -->\n    <script src=\"../uibuilder/vendor/vue/dist/vue.js\"></script> <!-- dev version with component compiler -->\n    <!-- <script src=\"../uibuilder/vendor/vue/dist/vue.min.js\"></script>   prod version with component compiler -->\n    <!-- <script src=\"../uibuilder/vendor/vue/dist/vue.runtime.min.js\"></script>   prod version without component compiler -->\n    <script src=\"../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.js\"></script>\n    <!-- Loading from CDN -->\n    <!-- <script src=\"https://unpkg.com/http-vue-loader\"></script> -->\n    <!-- Loading from npm installed version -->\n    <script src=\"../uibuilder/vendor/http-vue-loader/src/httpVueLoader.js\"></script>\n    \n    <!-- REQUIRED: Sets up Socket listeners and the msg object -->\n    <!-- <script src=\"./uibuilderfe.js\"></script>   //dev version -->\n    <script src=\"./uibuilderfe.min.js\"></script> <!--    //prod version -->\n    <!-- OPTIONAL: You probably want this. Put your custom code here -->\n    <script src=\"./index.js\"></script>\n</body>\n</html>\n\n","x":380,"y":1260,"wires":[],"icon":"node-red/parser-html.svg"},{"id":"9bd5106a.eb76","type":"comment","z":"18cb249f.38bafb","g":"636c4f3f.9afb9","name":"index.js","info":"'use strict'\n\nnew Vue({\n  el: '#app',\n  components: {\n    'acc-switch': httpVueLoader('acc-switch.vue'),\n  },\n  \n  data: {\n    msg: {},\n    accessories: {},\n    v_value: {},\n    debug: false\n  },\n  \n  methods: {    \n  \n  },\n\n  mounted: function() {\n    //console.debug('[mounted] app mounted - setting up uibuilder watchers')\n    uibuilder.start()\n    \n    uibuilder.onChange('msg', function(msg) {\n      this.msg = msg\n      \n      if (msg.payload.value !== this.v_value[msg.payload.name]) {\n        console.debug('[uibuilder.onChange]', msg.payload)\n        Vue.set(this.accessories, msg.payload.name, msg.payload)\n        Vue.set(this.v_value, msg.payload.name, msg.payload.value)\n      }\n    }.bind(this))\n  }\n})\n","x":510,"y":1260,"wires":[],"icon":"font-awesome/fa-file-code-o"},{"id":"a362192e.f34228","type":"comment","z":"18cb249f.38bafb","g":"636c4f3f.9afb9","name":"acc-switch.vue","info":"<template>\n  <div>\n    <b-form-checkbox\n      v-bind:checked=\"checked\"\n      v-on:change.native=\"sw_onChange($event.target.checked)\"\n      switch size=\"lg\"\n      >{{name}}\n      <b-badge v-if=\"badge\" variant=\"info\">{{checked? 'on': 'off'}}</b-badge>\n    </b-form-checkbox>\n  </div>\n</template>\n\n<script>\nmodule.exports = {\n  model: {\n    prop: 'checked',\n    event: 'change'\n  },\n  props: {\n    checked: Boolean,\n    name: String,\n    badge: {type: Boolean, default: false},\n  },\n  \n  data: function() {\n    return {\n    \n    }\n  },\n\n  methods: {\n    sw_onChange: function(value) {\n      console.debug('[sw_onChange]',this.name, value)\n      uibuilder.send({\n        \"topic\": \"sw_onChange\",\n        \"payload\": {\n          \"name\": this.name,\n          \"service_name\": this.name,\n          \"characteristic\":\"On\",\n          \"value\": value\n        }\n      })\n    }\n  },\n}\n</script>","x":660,"y":1260,"wires":[],"icon":"node-red/hash.svg"},{"id":"c8bec69.6d66c38","type":"comment","z":"18cb249f.38bafb","g":"636c4f3f.9afb9","name":"Cache Example: Instructions","info":"Many thanks to cflurin for this example.\n\n## To install this example:\n\n1. Install `node-red-contrib-uibuilder` & import\n  from the library\n\n2. In the uibuilder Front-End Library Manager \n  add `http-vue-loader`\n\n3. Copy the code from the comment nodes \n  (index.htm and index.js) and paste into the \n  corresponding files.\n\n4. Copy the code from the comment node \n  acc-switch.vue and paste into a new file \n  `acc-switch.vue`.\n\n5. Change the name of the cache variable if copying this function.\n\nSee here for further details: https://discourse.nodered.org/t/front-end-based-on-uibuilder-1-component-acc-switch-vue/14962\n\nAuthor: https://discourse.nodered.org/u/cflurin\n\n## To try\n\n1. Click one of the inject nodes and watch \nthe \"At Home\" switch in the ui change.\n\n2, Click any of the switches in the ui and\nsee the debug output.\n\n3. Having set some of the switches, load a new\nbrowser tab (or a different browser) to the same\nui page and note that the cache ensures that\nall of the switches are the same.\n\n4. Change a switch on one of the tabs and note\nthat the other tab also changes.\n\n## Other things to try\n\nTry altering the cache function so that different \nbrowser tabs can have different switch settings.\n\nWhile this doesn't make particular sense for \nthis example as written, it is good to know\nthat you can do it if needed.\n\nHint: You will need to retain the socket ID.","x":180,"y":1260,"wires":[]},{"id":"a87ef10c.5f61a","type":"inject","z":"18cb249f.38bafb","g":"636c4f3f.9afb9","name":"","repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"str","x":95,"y":1420,"wires":[["6c9d6822.3667c8"]],"l":false},{"id":"6c9d6822.3667c8","type":"change","z":"18cb249f.38bafb","g":"636c4f3f.9afb9","name":"Clear Cache","rules":[{"t":"set","p":"cacheControl","pt":"msg","to":"CLEAR","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":230,"y":1420,"wires":[["6327f550.24f41c"]]},{"id":"6327f550.24f41c","type":"link out","z":"18cb249f.38bafb","g":"636c4f3f.9afb9","name":"","links":["b912e4ce.5a69e8"],"x":380,"y":1420,"wires":[]}]

Here is an example that is rather simpler front-end code

[{"id":"4668b662.961658","type":"group","z":"18cb249f.38bafb","style":{"stroke":"#999999","stroke-opacity":"1","fill":"none","fill-opacity":"1"},"nodes":["ece9345a.f21808","bad4b165.e6a08","ba6581d8.f5fa2","bf35e77f.4a7978","5b8a68be.588ec8","4a7cc95e.3becd8","1a004a18.d3c5e6","ddf41d65.687","eb406f24.13a48","fa66e5de.8a81e8","83b1be67.1e3d3","c65760d4.37dac","a7683dd2.9a4e5","8cfb4d87.519ba","6eb39afc.539d54","1d092359.465b4d","8ed2c6e8.a5e258","c0567834.0593b8"],"x":44,"y":1939,"w":942,"h":302},{"id":"ece9345a.f21808","type":"debug","z":"18cb249f.38bafb","g":"4668b662.961658","name":"simple-debug","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":640,"y":2080,"wires":[]},{"id":"bad4b165.e6a08","type":"uibuilder","z":"18cb249f.38bafb","g":"4668b662.961658","name":"Simple Example","topic":"","url":"uib_simple","fwdInMessages":false,"allowScripts":false,"allowStyles":false,"copyIndex":true,"showfolder":false,"useSecurity":false,"sessionLength":"","tokenAutoExtend":false,"x":430,"y":2100,"wires":[["ece9345a.f21808"],["bf35e77f.4a7978","6eb39afc.539d54"]]},{"id":"ba6581d8.f5fa2","type":"inject","z":"18cb249f.38bafb","g":"4668b662.961658","name":"Manual Start","props":[{"p":"payload","v":"","vt":"str"},{"p":"topic","v":"","vt":"string"}],"repeat":"","crontab":"*/30 9-22 * * *","once":true,"onceDelay":"","topic":"","payload":"","payloadType":"str","x":105,"y":2100,"wires":[["eb406f24.13a48"]],"l":false},{"id":"bf35e77f.4a7978","type":"debug","z":"18cb249f.38bafb","g":"4668b662.961658","name":"uib controls","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":630,"y":2120,"wires":[]},{"id":"5b8a68be.588ec8","type":"comment","z":"18cb249f.38bafb","g":"4668b662.961658","name":"uibuilder/Simple Example","info":"[Front-End](/uib_simple)\n\nThis flow gets a \"quote of the day\" from the Internet and passes it\nto uibuilder. It caches the result so that if you reload the page,\nyou get the last result back. The quote is updated every 30 minutes\nduring the day and evening.\n\n\"Simple\" refers to the front-end code. While the flow looks a little\ncomplex, it really isn't. A trigger (repeating), an Internet request,\na cache and uibuilder. The link nodes loop the control output from\nuibuilder back to the cache.\n\n## Configuration\n\nUpdate the files:\n\n* `index.html`\n* `index.js`\n* `index.css`\n\nAccording to the example(s) in the 3 other comment nodes in this example.\n\nPress the button on the trigger to start the flow.","x":465,"y":2040,"wires":[]},{"id":"4a7cc95e.3becd8","type":"comment","z":"18cb249f.38bafb","g":"4668b662.961658","name":"index.html","info":"<!doctype html>\n<html lang=\"en\"><head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    <meta name=\"viewport\" content=\"width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes\">\n\n    <title>uibuilder simple example</title>\n    <meta name=\"description\" content=\"Node-RED uibuilder - Simple example using VueJS\">\n\n    <link rel=\"icon\" href=\"./images/node-blue.ico\">\n\n    <!-- Put your own custom styles in here -->\n    <link rel=\"stylesheet\" href=\"./index.css\" media=\"all\">\n\n</head><body>\n    <!-- The \"app\" element is where the code for dynamic updates is attached -->\n\t<div id=\"app\">\n\t    <h1>A simple uibuilder page</h1>\n\t    <p>\n\t        The elements below are dynamically updated when you send\n\t        a msg to your uibuilder node.\n\t    </p>\n\t    \n\t    <div v-if=\"msg.payload\">\n\t        <h2>Quote of the Day</h2>\n\t        <blockquote>\n\t            <i>{{ msg.payload.quote.body }}</i>\n    \t        <div>{{ msg.payload.quote.author }}</div>\n\t        </blockquote>\n\t    </div>\n\t    \n\t    <h2>The full msg object</h2>\n\t\t<code>{{ msg }}</code>\n\t\t\n\t</div>\n\n    <!-- Vendor Libraries - Load in the right order -->\n    <script src=\"../uibuilder/vendor/socket.io/socket.io.js\"></script>\n    <script src=\"../uibuilder/vendor/vue/dist/vue.min.js\"></script>\n\n    <!-- REQUIRED: Sets up Socket listeners, the uibuilder and msg objects -->\n    <script src=\"./uibuilderfe.min.js\"></script>\n\n    <!-- Put your own custom code in here -->\n    <script src=\"./index.js\"></script>\n\n</body></html>","x":640,"y":2040,"wires":[],"icon":"node-red/parser-html.svg"},{"id":"1a004a18.d3c5e6","type":"comment","z":"18cb249f.38bafb","g":"4668b662.961658","name":"index.js","info":"/* jshint browser: true, esversion: 5, asi: true */\n/*globals uibuilder, Vue */\n// @ts-nocheck\n/*\n  Copyright (c) 2019 Julian Knight (Totally Information)\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n*/\n'use strict'\n\n/** @see https://github.com/TotallyInformation/node-red-contrib-uibuilder/wiki/Front-End-Library---available-properties-and-methods */\n\nvar app1 = new Vue({\n    // The HTML element to attach to\n\tel: '#app',\n\t/** Pre-defined data\n\t *  Anything defined here can be used in the HTML\n\t *  if you update it, the HTML will automatically update\n\t */\n\tdata: {\n\t\tmsg: '[Nothing Recieved Yet]',\n\t},\n\n    // This is called when Vue is fully loaded\n\tmounted: function() {\n\t    // Start up uibuilder\n\t\tuibuilder.start()\n\t\t\n\t\t// Keep a convenient reference to the Vue app\n\t\tvar vueApp = this\n\n        /** Triggered when the node on the Node-RED server\n         *  recieves a (non-control) msg\n         */\n\t\tuibuilder.onChange('msg', function(msg) {\n\t\t\tvueApp.msg = msg\n\t\t})\n\n\t\t// Send message back to node-red\n\t\t// uibuilder.send({payload:'some message'})\n\n\t\t// Triggered on reciept of a control message from node-red\n\t\t//uibuilder.onChange('ctrlMsg', function(msg) {\n\t\t//    console.log(msg)\n\t\t//})\n\t},\n\t\n}) // --- End of the Vue app definition --- //\n\n// EOF","x":770,"y":2040,"wires":[],"icon":"font-awesome/fa-code"},{"id":"ddf41d65.687","type":"debug","z":"18cb249f.38bafb","g":"4668b662.961658","name":"qod-debug","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":430,"y":2160,"wires":[]},{"id":"eb406f24.13a48","type":"http request","z":"18cb249f.38bafb","g":"4668b662.961658","name":"GET","method":"GET","ret":"obj","paytoqs":false,"url":"https://favqs.com/api/qotd","tls":"","persist":false,"proxy":"","authType":"","x":230,"y":2100,"wires":[["ddf41d65.687","8cfb4d87.519ba","bad4b165.e6a08"]]},{"id":"fa66e5de.8a81e8","type":"comment","z":"18cb249f.38bafb","g":"4668b662.961658","name":"index.css","info":"body {font-family: sans-serif; font-size: 120%;}\ndiv, p, code { margin:0.3em; padding: 0.3em;}\n\nblockquote i {\n    font-size: 1.5em; color:grey; font-style: italic;\n}","x":900,"y":2040,"wires":[],"icon":"node-red/hash.svg"},{"id":"83b1be67.1e3d3","type":"link out","z":"18cb249f.38bafb","g":"4668b662.961658","name":"uib_simple_ctrl_out","links":["c65760d4.37dac"],"x":875,"y":2160,"wires":[]},{"id":"c65760d4.37dac","type":"link in","z":"18cb249f.38bafb","g":"4668b662.961658","name":"uib_simple_ctrl_in","links":["83b1be67.1e3d3"],"x":105,"y":2140,"wires":[["eb406f24.13a48"]]},{"id":"a7683dd2.9a4e5","type":"file","z":"18cb249f.38bafb","g":"4668b662.961658","name":"","filename":"","appendNewline":true,"createDir":false,"overwriteFile":"false","encoding":"none","x":650,"y":1980,"wires":[[]]},{"id":"8cfb4d87.519ba","type":"change","z":"18cb249f.38bafb","g":"4668b662.961658","name":"","rules":[{"t":"set","p":"filename","pt":"msg","to":"\"./quotes/\" & payload.quote.id & \".json\"","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":470,"y":1980,"wires":[["a7683dd2.9a4e5"]]},{"id":"6eb39afc.539d54","type":"switch","z":"18cb249f.38bafb","g":"4668b662.961658","name":"client only","property":"from","propertyType":"msg","rules":[{"t":"eq","v":"client","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":600,"y":2160,"wires":[["1d092359.465b4d"]]},{"id":"1d092359.465b4d","type":"change","z":"18cb249f.38bafb","g":"4668b662.961658","name":"delete","rules":[{"t":"delete","p":"cacheControl","pt":"msg"},{"t":"delete","p":"uibuilderCtrl","pt":"msg"},{"t":"delete","p":"from","pt":"msg"},{"t":"delete","p":"_socketId","pt":"msg"},{"t":"delete","p":"_msgid","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":770,"y":2160,"wires":[["83b1be67.1e3d3"]]},{"id":"8ed2c6e8.a5e258","type":"comment","z":"18cb249f.38bafb","g":"4668b662.961658","name":"Saves copies of all quotes ==>","info":"To a `quotes` folder.","x":230,"y":1980,"wires":[]},{"id":"c0567834.0593b8","type":"comment","z":"18cb249f.38bafb","g":"4668b662.961658","name":"Shows a new quote if the page is reloaded","info":"","x":720,"y":2200,"wires":[]}]

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.