Uibuilder: question how to build a switch

I'm trying to build a switch with uibuilder (bootstrap-vue).
My goal is to be able to set the switch on 2 browsers (iPhone and mac) and from node-red (uibuilder input).

uibuilder1

[{"id":"f38da159.cd70f","type":"uibuilder","z":"1d3866f1.0e8e09","name":"","topic":"","url":"uibuilder","fwdInMessages":false,"allowScripts":false,"allowStyles":false,"copyIndex":true,"showfolder":false,"x":380,"y":640,"wires":[["ec349a33.5ac878"],[]]},{"id":"1baa6671.01fc4a","type":"inject","z":"1d3866f1.0e8e09","name":"true","topic":"","payload":"{\"name\":\"at_home\",\"service_name\":\"at_home\",\"characteristic\":\"On\",\"value\":true}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":190,"y":620,"wires":[["f38da159.cd70f"]]},{"id":"ec349a33.5ac878","type":"debug","z":"1d3866f1.0e8e09","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":570,"y":640,"wires":[]},{"id":"507ad928.308538","type":"inject","z":"1d3866f1.0e8e09","name":"false","topic":"","payload":"{\"name\":\"at_home\",\"service_name\":\"at_home\",\"characteristic\":\"On\",\"value\":false}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":190,"y":680,"wires":[["f38da159.cd70f"]]}]

It works with one exception:

  1. I can set the switch in any browser and get the msg on output 1.
  2. I can set the switch in node-red and send the msg to all browsers.
  3. however if I set the switch in one browser, the second browser isn't updated.

Any tips how I can achieve it?

Versions:

  • uibuilder v2.01 (boostrap-vue 2.0.0-rc.28 instead of 2.0.0-rc.27)
  • node-red v0.20.7

index.html

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes">

    <title>Node-RED UI Builder</title>
    <meta name="description" content="Node-RED UI Builder - VueJS + bootstrap-vue version">

    <link rel="icon" href="./images/node-blue.ico">

    <!-- See https://goo.gl/OOhYW5 -->
    <link rel="manifest" href="./manifest.json">
    <meta name="theme-color" content="#3f51b5">
    <link type="text/css" rel="stylesheet" href="../uibuilder/vendor/bootstrap/dist/css/bootstrap.min.css" />
    <link type="text/css" rel="stylesheet" href="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.css" />
    
    <link rel="stylesheet" href="./index.css" media="all">

</head>
<body>
    <div id="app">
      <b-container id="app_container">             
        <b-card no-body>
          <b-tabs card small>
            <b-tab title="Main" active>
              <b-form-checkbox v-model="at_home_value" @input="at_home_input" switch size="lg" class="mb-2">
                at home <b-badge variant="info">{{at_home_value}}</b-badge>
              </b-form-checkbox>
              <b-form-checkbox v-model="ss_value" @input="ss_input" switch size="lg" class="mb-2">
                security system <b-badge variant="info">{{ss_value}}</b-badge>
              </b-form-checkbox>
            </b-tab>

            <b-tab title="Rooms">
              <b-card-text>rooms ...</b-card-text>
            </b-tab>
            
            <b-tab title="Heating">
              <b-card-text>heating ...</b-card-text>
            </b-tab>
            
            <b-tab title="Greenhouse">
              <b-card-text>greenhouse ...</b-card-text>
            </b-tab>
          </b-tabs>
        </b-card>
      </b-container>
    </div>

    <script src="../uibuilder/vendor/socket.io/socket.io.js"></script>

    <!--  Vendor Libraries - Load in the right order -->
    <script src="../uibuilder/vendor/vue/dist/vue.js"></script> <!-- dev version with component compiler -->
    <!-- <script src="../uibuilder/vendor/vue/dist/vue.min.js"></script>   prod version with component compiler -->
    <!-- <script src="../uibuilder/vendor/vue/dist/vue.runtime.min.js"></script>   prod version without component compiler -->
    <script src="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.js"></script>

    <!-- REQUIRED: Sets up Socket listeners and the msg object -->
    <!-- <script src="./uibuilderfe.js"></script>   //dev version -->
    <script src="./uibuilderfe.min.js"></script> <!--    //prod version -->
    <!-- OPTIONAL: You probably want this. Put your custom code here -->
    <script src="./index.js"></script>
</body>
</html>

index.js

// @ts-nocheck
'use strict'

var app1 = new Vue({
    el: '#app',
    data: {
        at_home_value: null,
        at_home_flag: true,
        ss_value: null,
        ss_flag: true,
        
        socketConnectedState : false,
        serverTimeOffset     : '[unknown]',
    },
    
    methods: {
        at_home_input: function() {
          //console.info('[indexjs:at_home_input] flag: ' + this.at_home_flag + ' value: ' + this.at_home_value);    
          if (this.at_home_flag) {
            uibuilder.send( {
            "topic": "uibuilder",
            "payload": {
              "name": "at_home",
              "service_name":"at_home",
              "characteristic":"On",
              "value": this.at_home_value
            }
          })
          } else {
            this.at_home_flag = true;
          }
        },
        
        ss_input: function() {
        if (this.ss_flag) {
          uibuilder.send( {
            "topic": "uibuilder",
            "payload": {
              "name": "security_system",
              "service_name":"security_system",
              "characteristic":"On",
              "value": this.ss_value
            }
          })
          } else {
            this.ss_flag = true;
          }
        },
    },

    mounted: function(){
        //console.debug('[indexjs:Vue.mounted] app mounted - setting up uibuilder watchers')
        uibuilder.start()

        var vueApp = this

        uibuilder.onChange('msg', function(newVal){
            //console.info('[indexjs:uibuilder.onChange] msg received from Node-RED server:', newVal)            
            switch (newVal.payload.name) {
              case "at_home":
                vueApp.at_home_value = newVal.payload.value;
                vueApp.at_home_flag = false;
                break;
              case "security_system":
                vueApp.ss_value = newVal.payload.value;
                vueApp.ss_flag = false;
                break;              
            }
        })
    }
})

This might be related to that the state in one browser isn't shared to all other connected browsers directly. Have you tried connecting the output of the uibuilder node back into the input of the node, potentially with some filtering code in between? That way the actions in one browser are reflected onto another browser through NR. Because Vue in itself won't do that for you.

Yes, I did, it seems to be simple but I didn't find a solution yet.

I know Vue won't do it, but I can imagine uibuilder can do that for me.

It can, if you make sure the message arrives back in, so that the other client knows something happened. You could use the uibuilderfe.onChange('msg') function for that, to have it connect back to the page state and work it in.
What I mean is that you have to add the logic to handle this yourself. Uibuilder is rather bare bones, which is a good thing as it gives you all freedom to add the logic and interface the way you want it to happen. On the other hand it means that any logic for reflecting changes to multiple clients has to be written by yourself.

1 Like

Okey, I'm trying to implement it.

This way works but there must be an easier solution:

[{"id":"f38da159.cd70f","type":"uibuilder","z":"1d3866f1.0e8e09","name":"","topic":"","url":"uibuilder","fwdInMessages":false,"allowScripts":false,"allowStyles":false,"copyIndex":true,"showfolder":false,"x":380,"y":660,"wires":[["ca27963.2038d68","29d38966.242f86"],[]]},{"id":"1baa6671.01fc4a","type":"inject","z":"1d3866f1.0e8e09","name":"true","topic":"","payload":"{\"name\":\"at_home\",\"service_name\":\"at_home\",\"characteristic\":\"On\",\"value\":true}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":190,"y":640,"wires":[["f38da159.cd70f"]]},{"id":"ec349a33.5ac878","type":"debug","z":"1d3866f1.0e8e09","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":690,"y":660,"wires":[]},{"id":"507ad928.308538","type":"inject","z":"1d3866f1.0e8e09","name":"false","topic":"","payload":"{\"name\":\"at_home\",\"service_name\":\"at_home\",\"characteristic\":\"On\",\"value\":false}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":190,"y":700,"wires":[["f38da159.cd70f"]]},{"id":"2d8f28e7.0fc0d8","type":"link in","z":"1d3866f1.0e8e09","name":"home-replay","links":["88858cff.40d33"],"x":215,"y":600,"wires":[["f38da159.cd70f"]]},{"id":"88858cff.40d33","type":"link out","z":"1d3866f1.0e8e09","name":"home-controls","links":["2d8f28e7.0fc0d8"],"x":735,"y":600,"wires":[]},{"id":"29d38966.242f86","type":"change","z":"1d3866f1.0e8e09","name":"","rules":[{"t":"delete","p":"_socketId","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":600,"y":600,"wires":[["88858cff.40d33"]]},{"id":"ca27963.2038d68","type":"rbe","z":"1d3866f1.0e8e09","name":"","func":"rbe","gap":"","start":"","inout":"out","property":"payload","x":550,"y":660,"wires":[["ec349a33.5ac878"]]}]

index.html

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes">

    <title>Node-RED UI Builder</title>
    <meta name="description" content="Node-RED UI Builder - VueJS + bootstrap-vue version">

    <link rel="icon" href="./images/node-blue.ico">

    <!-- See https://goo.gl/OOhYW5 -->
    <link rel="manifest" href="./manifest.json">
    <meta name="theme-color" content="#3f51b5">
    <link type="text/css" rel="stylesheet" href="../uibuilder/vendor/bootstrap/dist/css/bootstrap.min.css" />
    <link type="text/css" rel="stylesheet" href="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.css" />
    
    <link rel="stylesheet" href="./index.css" media="all">

</head>
<body>
    <div id="app">
      <b-container id="app_container">             
        <b-card no-body>
          <b-tabs card small>
            <b-tab title="Main" active>
              <b-form-checkbox v-model="at_home_value" @input="at_home_input" switch size="lg" class="mb-2">
                at home <b-badge variant="info">{{at_home_value}}</b-badge>
              </b-form-checkbox>
              <b-form-checkbox v-model="ss_value" @input="ss_input" switch size="lg" class="mb-2">
                security system <b-badge variant="info">{{ss_value}}</b-badge>
              </b-form-checkbox>
            </b-tab>

            <b-tab title="Rooms">
              <b-card-text>rooms ...</b-card-text>
            </b-tab>
            
            <b-tab title="Heating">
              <b-card-text>heating ...</b-card-text>
            </b-tab>
            
            <b-tab title="Greenhouse">
              <b-card-text>greenhouse ...</b-card-text>
            </b-tab>
          </b-tabs>
        </b-card>
      </b-container>
    </div>

    <script src="../uibuilder/vendor/socket.io/socket.io.js"></script>

    <!--  Vendor Libraries - Load in the right order -->
    <script src="../uibuilder/vendor/vue/dist/vue.js"></script> <!-- dev version with component compiler -->
    <!-- <script src="../uibuilder/vendor/vue/dist/vue.min.js"></script>   prod version with component compiler -->
    <!-- <script src="../uibuilder/vendor/vue/dist/vue.runtime.min.js"></script>   prod version without component compiler -->
    <script src="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.js"></script>

    <!-- REQUIRED: Sets up Socket listeners and the msg object -->
    <!-- <script src="./uibuilderfe.js"></script>   //dev version -->
    <script src="./uibuilderfe.min.js"></script> <!--    //prod version -->
    <!-- OPTIONAL: You probably want this. Put your custom code here -->
    <script src="./index.js"></script>
</body>
</html>

index.js

// @ts-nocheck
'use strict'

var app1 = new Vue({
    el: '#app',
    data: {
        at_home_value: null,
        ss_value: null,
        
        socketConnectedState : false,
        serverTimeOffset     : '[unknown]',
    },
    
    methods: {
        at_home_input: function() {   
            //console.info('[indexjs:at_home_input] value: ' + this.at_home_value); 
            uibuilder.send( {
            "topic": "uibuilder",
            "payload": {
              "name": "at_home",
              "service_name":"at_home",
              "characteristic":"On",
              "value": this.at_home_value
            }
          })
        },
        
        ss_input: function() {
          uibuilder.send( {
            "topic": "uibuilder",
            "payload": {
              "name": "security_system",
              "service_name":"security_system",
              "characteristic":"On",
              "value": this.ss_value
            }
          })
        },
    },

    mounted: function(){
        //console.debug('[indexjs:Vue.mounted] app mounted - setting up uibuilder watchers')
        uibuilder.start()

        var vueApp = this

        uibuilder.onChange('msg', function(newVal){
          //console.info('[uibuilder.onChange] msg ', newVal)            
          switch (newVal.payload.name) {
            case "at_home":
              vueApp.at_home_value = newVal.payload.value;
              break;
            case "security_system":
              vueApp.ss_value = newVal.payload.value;
              break;            
          }
        })
    }
})
1 Like

Well, if you look at your flow, you will find that the solution is actually very simple. You remove the socketid and republish which broadcasts to all connected clients. You could have a thousand clients connected without any more complexity. :wink:

I think this is the same with Dashboard actually?

You could enhance this by adding a second entry to your change node to add a flag then test the flag in your front-end code so that it doesn't send the same message except from the first browser that does the switch. I think that should work. Then you should be able to remove the rbe node.

uibuilder is designed to let you build anything you can imagine. So it is somewhat inevitable that you will see some slightly more complex code or flows in places.

Another possible enhancement would be to track connected browsers and limit the "spread" of the broadcast. With that though, you have to be careful about how Socket.IO works as it will assign a new socketid if the user reloads the page.

Hi,
As I'm new to uibuilder I'm still learning how to use it.
My first step is to find a working solution and in a second step to search for simple and maintainable code.

Thanks for your tips, let's see how I can improve my first version.
I've just added the cache function which I need anyway so at the end it will be - as you said - very simple. :slight_smile:

1 Like

With Uibuilder v2 only just released, and it being the first version to come with Vue as default framework you won’t find much ready solutions I’m afraid. I’ve a few, but almost none configured for SocketIO plus NR yet. So that has the logic in state machines with Vuex mostly on the client side.

Always nice to see new people trying it out. :smile:

Sensible.

Sure, always happy to help so add to this thread if you need more. You will find that, although I've built the node, it doesn't necessarily mean I know the best ways to use it!

Lena has far more experience than I do with Vue I think, I'm very much a dabbler.

Also, the WIKI is always open for contributions :wink: Feel free to add something, I can tidy it up later if needed. I've not yet had the time to update the existing examples or to add any new ones. It is on my to-do list.

Another thing on my list - to turn that function into a node. Though the function does have some advantages - you can tune it to work exactly how you like. But a more friendly node needs to be added when I can find time.

Do keep asking, I'll do my best to answer questions and help you find answers.

No problems for me, that means I'm free to create a front-end based on my needs.
I just started with Vue and BootstrapVue, the docs are good and it seems easy to learn.

I agree, I prefer to use the function, actually I'm going to change it. All my devices are defined using the HAP (HomeKit Accessory Protocol) like the example:
{"name":"at_home","service_name":"at_home","characteristic":"On","value":true}, so I'll use the name for the key.

1 Like

I tried using a flag but it's quite complex and almost impossible to cover all sequences.
I'm searching for an event modifier (like .self or similar) which only triggers on change from the front-end. Any ideas?

My latest version so far (update)

switch

[{"id":"f38da159.cd70f","type":"uibuilder","z":"1d3866f1.0e8e09","name":"","topic":"","url":"uibuilder","fwdInMessages":false,"allowScripts":false,"allowStyles":false,"copyIndex":true,"showfolder":false,"x":360,"y":660,"wires":[["e61155ef.893498","88858cff.40d33"],["ce240b22.6004a8"]]},{"id":"1baa6671.01fc4a","type":"inject","z":"1d3866f1.0e8e09","name":"true","topic":"","payload":"{\"name\":\"at_home\",\"service_name\":\"at_home\",\"characteristic\":\"On\",\"value\":true}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":640,"wires":[["1f643e31.8a1492"]]},{"id":"ec349a33.5ac878","type":"debug","z":"1d3866f1.0e8e09","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":790,"y":640,"wires":[]},{"id":"507ad928.308538","type":"inject","z":"1d3866f1.0e8e09","name":"false","topic":"","payload":"{\"name\":\"at_home\",\"service_name\":\"at_home\",\"characteristic\":\"On\",\"value\":false}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":680,"wires":[["1f643e31.8a1492"]]},{"id":"2d8f28e7.0fc0d8","type":"link in","z":"1d3866f1.0e8e09","name":"home-replay","links":["88858cff.40d33","ce240b22.6004a8"],"x":115,"y":600,"wires":[["1f643e31.8a1492"]]},{"id":"88858cff.40d33","type":"link out","z":"1d3866f1.0e8e09","name":"home-controls","links":["2d8f28e7.0fc0d8"],"x":475,"y":600,"wires":[]},{"id":"ce240b22.6004a8","type":"link out","z":"1d3866f1.0e8e09","name":"","links":["ae0fd0a1.2f9f7","2d8f28e7.0fc0d8"],"x":475,"y":720,"wires":[]},{"id":"9090752a.336f98","type":"rbe","z":"1d3866f1.0e8e09","name":"","func":"rbe","gap":"","start":"","inout":"out","property":"payload","x":650,"y":640,"wires":[["ec349a33.5ac878"]]},{"id":"1f643e31.8a1492","type":"function","z":"1d3866f1.0e8e09","name":"cache","func":"// saved context\nvar ui_msgs = context.get('ui_msgs') || {}\n\n// Replay cache if requested\nif (msg.hasOwnProperty('uibuilderCtrl') && msg.uibuilderCtrl === 'ready for content') {\n    for (var name in ui_msgs) {\n        node.send({\n            \"topic\": msg.topic,\n            \"payload\": ui_msgs[name],\n            \"_socketId\": msg._socketId\n        })\n    }\n    return null\n}\n\n// ignore cacheControl and uibuilder control messages\nif (msg.hasOwnProperty('cacheControl') || msg.hasOwnProperty('uibuilderCtrl')) return null\n\n// Keep the last msg.payload by msg.payload.name\nui_msgs[msg.payload.name] = msg.payload\n\n// save context for next time\ncontext.set('ui_msgs', ui_msgs)\n\nmsg._socketId = null\n\nreturn msg;\n","outputs":1,"noerr":0,"x":230,"y":660,"wires":[["f38da159.cd70f"]]},{"id":"e61155ef.893498","type":"switch","z":"1d3866f1.0e8e09","name":"","property":"payload.name","propertyType":"msg","rules":[{"t":"eq","v":"at_home","vt":"str"},{"t":"eq","v":"security_system","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":3,"x":510,"y":660,"wires":[["9090752a.336f98"],["e001f6a0.f270d8"],["1785bd8.8e00a43"]]},{"id":"bf33f140.dc716","type":"debug","z":"1d3866f1.0e8e09","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":790,"y":680,"wires":[]},{"id":"e001f6a0.f270d8","type":"rbe","z":"1d3866f1.0e8e09","name":"","func":"rbe","gap":"","start":"","inout":"out","property":"payload","x":650,"y":680,"wires":[["bf33f140.dc716"]]},{"id":"1785bd8.8e00a43","type":"debug","z":"1d3866f1.0e8e09","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":720,"wires":[]}]

index.html

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes">

    <title>Node-RED UI Builder</title>
    <meta name="description" content="Node-RED UI Builder - VueJS + bootstrap-vue version">

    <link rel="icon" href="./images/node-blue.ico">

    <!-- See https://goo.gl/OOhYW5 -->
    <link rel="manifest" href="./manifest.json">
    <meta name="theme-color" content="#3f51b5">
    <link type="text/css" rel="stylesheet" href="../uibuilder/vendor/bootstrap/dist/css/bootstrap.min.css" />
    <link type="text/css" rel="stylesheet" href="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.css" />
    
    <link rel="stylesheet" href="./index.css" media="all">
</head>
<body>
    <div id="app" v-cloak>
      <b-container id="app_container">             
        <b-card no-body>
          <b-tabs card small>
            <b-tab title="Main" active>
              <b-form-checkbox v-model="at_home" v-on:change.native="switch_onChange('at_home', $event)" switch size="lg" class="mb-2">
                at home <b-badge variant="info">{{at_home}}</b-badge>
              </b-form-checkbox>
              <b-form-checkbox v-model="security_system" v-on:change.native="switch_onChange('security_system', $event)" switch size="lg" class="mb-2">
                security system <b-badge variant="info">{{security_system}}</b-badge>
              </b-form-checkbox>
            </b-tab>

            <b-tab title="Rooms">
              <b-card-text>rooms ...</b-card-text>
            </b-tab>
            
            <b-tab title="Heating">
              <b-card-text>heating ...</b-card-text>
            </b-tab>
            
            <b-tab title="Greenhouse">
              <b-card-text>greenhouse ...</b-card-text>
            </b-tab>
          </b-tabs>
        </b-card>
      </b-container>
    </div>

    <script src="../uibuilder/vendor/socket.io/socket.io.js"></script>

    <!--  Vendor Libraries - Load in the right order -->
    <script src="../uibuilder/vendor/vue/dist/vue.js"></script> <!-- dev version with component compiler -->
    <!-- <script src="../uibuilder/vendor/vue/dist/vue.min.js"></script>   prod version with component compiler -->
    <!-- <script src="../uibuilder/vendor/vue/dist/vue.runtime.min.js"></script>   prod version without component compiler -->
    <script src="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.js"></script>

    <!-- REQUIRED: Sets up Socket listeners and the msg object -->
    <!-- <script src="./uibuilderfe.js"></script>   //dev version -->
    <script src="./uibuilderfe.min.js"></script> <!--    //prod version -->
    <!-- OPTIONAL: You probably want this. Put your custom code here -->
    <script src="./index.js"></script>
</body>
</html>

index.js

'use strict'

var app1 = new Vue({
  el: '#app',
  data: {
      at_home: null,
      security_system: null,
      control: null,
      
      socketConnectedState : false,
      serverTimeOffset     : '[unknown]',
  },
  
  methods: {
    switch_onChange: function(name, event) {
      //console.debug(event)
      //console.debug('[switch_onChange] name: %s; value: %s',name, this[name])
      uibuilder.send({
        "payload": {
          "name": name,
          "service_name": name,
          "characteristic":"On",
          "value": this[name]
        }
      })
    },
  },

  mounted: function() {
    //console.debug('[indexjs:Vue.mounted] app mounted - setting up uibuilder watchers')
    uibuilder.start()

    uibuilder.onChange('msg', function(msg) {
      if (msg.payload.value !== this[msg.payload.name]) {
        //console.debug('[uibuilder.onChange]', msg)
        this[msg.payload.name] = msg.payload.value
      }
    }.bind(this))
  }
})

index.css

/*  Colours for Syntax Highlighted pre's */
.syntax-highlight {color:white;background-color:black;padding:5px 10px;}
.syntax-highlight > .key {color:#ffbf35}
.syntax-highlight > .string {color:#5dff39;}
.syntax-highlight > .number {color:#70aeff;}
.syntax-highlight > .boolean {color:#b993ff;}

[v-cloak] {
  display: none;
}