diff --git a/app/.eslintrc.js b/app/.eslintrc.js
index f0d6621694f34721996df7cf0505169f567861e5..ec030892531f4cb4b6c9219daf6cb2fa1fb3dc44 100644
--- a/app/.eslintrc.js
+++ b/app/.eslintrc.js
@@ -1,14 +1,14 @@
 module.exports = {
   root: true,
   env: {
-    node: true
+    node: true,
   },
   extends: ['plugin:vue/essential', 'eslint:recommended', '@vue/prettier'],
   parserOptions: {
-    parser: 'babel-eslint'
+    parser: 'babel-eslint',
   },
   rules: {
     'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
-    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
-  }
+    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
+  },
 }
diff --git a/app/.prettierrc.js b/app/.prettierrc.js
index a6b80c2e1549a84f55ed580436bb2cdea319d573..3f714ac456ec4e692b2d0c8eda13d60125bf2cf7 100644
--- a/app/.prettierrc.js
+++ b/app/.prettierrc.js
@@ -1,4 +1,4 @@
 module.exports = {
   singleQuote: true,
-  semi: false
+  semi: false,
 }
diff --git a/app/babel.config.js b/app/babel.config.js
index 716b0237c62f442f420a7104934af4cb31d52c39..c1b783eac1d57dcae03a6995db453eeeb1a7d0b6 100644
--- a/app/babel.config.js
+++ b/app/babel.config.js
@@ -1,3 +1,3 @@
 module.exports = {
-  presets: ['@vue/cli-plugin-babel/preset']
+  presets: ['@vue/cli-plugin-babel/preset'],
 }
diff --git a/app/src/components/ModesCardorg.vue b/app/src/components/ModesCardorg.vue
index 47e00c97771a11a925b72a68d91dffbd843e171b..ff11827995c8389e184ed53d8dea9cf13da31822 100644
--- a/app/src/components/ModesCardorg.vue
+++ b/app/src/components/ModesCardorg.vue
@@ -1,6 +1,6 @@
 <template>
   <div v-show="tipsplease">
-    <vue-draggable-resizable
+    <draggable
       class="innernode"
       :w="450"
       :h="590"
@@ -111,7 +111,7 @@
           <BaseButton buttonClass="danger" @click="hideTips()">Hide</BaseButton>
         </div>
       </div>
-    </vue-draggable-resizable>
+    </draggable>
     <div v-show="!tipsplease"></div>
   </div>
 </template>
@@ -119,8 +119,8 @@
 <script>
 import marked from 'marked'
 import { mapState } from 'vuex'
-
 import SvgButton from '@/components/SvgButton'
+import draggable from '@/experimental/Draggable'
 
 export default {
   data: function () {
@@ -148,6 +148,7 @@ export default {
   },
   components: {
     SvgButton,
+    draggable
   },
 }
 </script>
diff --git a/app/src/components/NodesLayer.vue b/app/src/components/NodesLayer.vue
index 2a8133ab5013e97b9530c1ad1d17d4f3b2d69682..7e6f784cd6b644426775e68c573c2637c0313adb 100644
--- a/app/src/components/NodesLayer.vue
+++ b/app/src/components/NodesLayer.vue
@@ -2,7 +2,7 @@
   <div ref="nodes" class="node">
     <div v-for="(value, index) in configPositions" v-bind:key="index">
       <div v-if="toolmode == 'move'">
-        <vue-draggable-resizable
+        <draggable
           class="innernode"
           v-if="nodeid == value.node_id && deleted == false"
           :w="value.width"
@@ -10,6 +10,7 @@
           :x="value.x_pos"
           :y="value.y_pos"
           :z="value.z_index"
+          :scale="scale"
           :draggable="false"
           :resizable="false"
           style="border: 2px dashed black; background-color: rgb(155, 194, 216)"
@@ -78,13 +79,13 @@
               </div> -->
             </div>
           </form>
-        </vue-draggable-resizable>
+        </draggable>
       </div>
 
       <!-- Same code as above when in any other mode other than move so you can drag nodes-->
 
       <div v-else>
-        <vue-draggable-resizable
+        <draggable
           class="innernode"
           v-if="nodeid == value.node_id && deleted == false"
           :w="value.width"
@@ -92,6 +93,7 @@
           :x="value.x_pos"
           :y="value.y_pos"
           :z="value.z_index"
+          :scale="scale"
           @activated="onActivated"
           @dragging="onDrag"
           @resizing="onResize"
@@ -164,7 +166,7 @@
               </div>
             </div>
           </form>
-        </vue-draggable-resizable>
+        </draggable>
       </div>
     </div>
   </div>
@@ -174,6 +176,8 @@
 import { mapState } from 'vuex'
 import marked from 'marked'
 //import lodash from 'lodash'
+import draggable from '@/experimental/Draggable'
+
 var readmode
 
 export default {
@@ -215,6 +219,7 @@ export default {
   // },
 
   computed: mapState({
+    scale: (state) => state.ui.scale,
     myNodes: (state) => state.myNodes,
     configPositions: (state) => state.configPositions,
     configConnections: (state) => state.configConnections,
@@ -392,6 +397,9 @@ export default {
       }
     },
   },
+  components: {
+    draggable,
+  },
 }
 </script>
 
@@ -458,6 +466,4 @@ li {
 /* .dragging {
   transform: scale(0.7);
 } */
-
-
 </style>
diff --git a/app/src/components/OnBoard.vue b/app/src/components/OnBoard.vue
index 74e5223b8bb35ad035f50389f073653d9a5ad536..877c6255ccd312d1b189d8702fd194319557f06a 100644
--- a/app/src/components/OnBoard.vue
+++ b/app/src/components/OnBoard.vue
@@ -1,7 +1,7 @@
 <template>
   <div ref="nodes" class="node">
     <div v-if="this.currentroute.name == 'Organise'">
-      <vue-draggable-resizable
+      <draggable
         class="innernode"
         :w="300"
         :h="335"
@@ -44,9 +44,9 @@
             </div>
           </div>
         </div>
-      </vue-draggable-resizable>
+      </draggable>
 
-      <vue-draggable-resizable
+      <draggable
         class="innernode"
         :w="310"
         :h="375"
@@ -84,11 +84,11 @@
             <h4>Loading...</h4>
           </div>
         </div>
-      </vue-draggable-resizable>
+      </draggable>
     </div>
     <div v-else>
       <div class="nodes">
-        <vue-draggable-resizable class="hide"></vue-draggable-resizable>
+        <draggable class="hide"></draggable>
         <p id="nodeid" :inner-html.prop="nodetext | marked"></p>
         <div v-if="name == false">
           <input
@@ -154,6 +154,7 @@
 </template>
 
 <script>
+import draggable from '@/experimental/Draggable'
 import Router from '@/router'
 import marked from 'marked'
 
@@ -220,6 +221,9 @@ export default {
       this.$refs.microcosm.focus()
     },
   },
+  components: {
+    draggable,
+  },
 }
 </script>
 
diff --git a/app/src/components/OtherNodeslayer.vue b/app/src/components/OtherNodeslayer.vue
index 352e039aca71b6e82b6b3fb6c79e15b081c1c708..1da4fcc0bd3ba40f3ae8ed691f333cecc7260d56 100644
--- a/app/src/components/OtherNodeslayer.vue
+++ b/app/src/components/OtherNodeslayer.vue
@@ -3,13 +3,14 @@
     <div v-for="(value, index) in configPositions" v-bind:key="index">
       <div v-if="toolmode == 'move'">
         <!-- make draggable false as we are panning around -->
-        <vue-draggable-resizable
+        <draggable
           v-if="nodeid == value.node_id && deleted == false"
           :w="value.width"
           :h="value.height"
           :x="value.x_pos"
           :y="value.y_pos"
           :z="value.z_index"
+          :scale="scale"
           @activated="onActivated"
           @dragging="onDrag"
           @resizing="onResize"
@@ -102,17 +103,18 @@
               </div> -->
             </div>
           </div>
-        </vue-draggable-resizable>
+        </draggable>
       </div>
 
       <div v-else>
-        <vue-draggable-resizable
+        <draggable
           v-if="nodeid == value.node_id && deleted == false"
           :w="value.width"
           :h="value.height"
           :x="value.x_pos"
           :y="value.y_pos"
           :z="value.z_index"
+          :scale="scale"
           @activated="onActivated"
           @dragging="onDrag"
           @resizing="onResize"
@@ -203,7 +205,7 @@
               </div> -->
             </div>
           </div>
-        </vue-draggable-resizable>
+        </draggable>
       </div>
     </div>
   </div>
@@ -213,11 +215,13 @@
 import { mapState } from 'vuex'
 import EmojiPicker from 'vue-emoji-picker'
 import marked from 'marked'
+import draggable from '@/experimental/Draggable'
 
 export default {
   name: 'otherNodeslayer',
 
   components: {
+    draggable,
     EmojiPicker,
   },
   props: {
@@ -242,6 +246,7 @@ export default {
 
   mounted() {},
   computed: mapState({
+    scale: (state) => state.ui.scale,
     otherNodes: (state) => state.otherNodes,
     configPositions: (state) => state.configPositions,
     configConnections: (state) => state.configConnections,
diff --git a/app/src/components/TipsLayer.vue b/app/src/components/TipsLayer.vue
index 915592e1e798db665f51ee9c155fd08774e2d723..d9f9095b0d18cc7df3a7ffc6e84a673355a0c9fa 100644
--- a/app/src/components/TipsLayer.vue
+++ b/app/src/components/TipsLayer.vue
@@ -1,7 +1,7 @@
 <template>
   <div ref="nodes" class="node">
     <div v-show="tipsplease">
-      <vue-draggable-resizable
+      <draggable
         class="innernode"
         :w="250"
         :h="225"
@@ -21,7 +21,7 @@
             >
           </div>
         </div>
-      </vue-draggable-resizable>
+      </draggable>
     </div>
 
     <div v-show="!tipsplease"></div>
@@ -30,7 +30,7 @@
 
 <script>
 import marked from 'marked'
-
+import draggable from '@/experimental/Draggable'
 export default {
   data: function () {
     return {
@@ -50,6 +50,9 @@ export default {
       this.tipsplease = false
     },
   },
+  components: {
+    draggable,
+  },
 }
 </script>
 
diff --git a/app/src/components/ToolBar.vue b/app/src/components/ToolBar.vue
index 2e2b4422b8f8a96c882f087ccdbb761fdcd9c7f8..d6b25d08c0f9b30d6d77e23a8f975a620ef9254b 100644
--- a/app/src/components/ToolBar.vue
+++ b/app/src/components/ToolBar.vue
@@ -1,6 +1,6 @@
 <template>
   <div ref="nodes" class="node">
-    <vue-draggable-resizable
+    <draggable
       class="innernode"
       :w="127"
       :h="84"
@@ -27,15 +27,19 @@
           <line class="c" y2="41" transform="translate(1426.5 863.5)" />
         </g>
       </svg>
-    </vue-draggable-resizable>
+    </draggable>
   </div>
 </template>
 
 <script>
+import draggable from '@/experimental/Draggable'
 export default {
   data: function () {
     return {}
   },
+  components: {
+    draggable,
+  },
 }
 </script>
 
diff --git a/app/src/components/old/drag.js b/app/src/components/old/drag.js
index d270cb77d59810055e4d71631e9e81e821ba28d9..04c94bb47a3e6a581059c64915b59585eca4da7c 100644
--- a/app/src/components/old/drag.js
+++ b/app/src/components/old/drag.js
@@ -60,6 +60,6 @@ export const drag = {
       function setTranslate(xPos, yPos, el) {
         el.style.transform = 'translate3d(' + xPos + 'px, ' + yPos + 'px, 0)'
       }
-    }
-  }
+    },
+  },
 }
diff --git a/app/src/components/old/draw.js b/app/src/components/old/draw.js
index 9bd5dc3920c911698a80b58d605f5549795cd87c..2381617ce04759ac818c80988f94163e83165689 100644
--- a/app/src/components/old/draw.js
+++ b/app/src/components/old/draw.js
@@ -25,6 +25,6 @@ export const draw = {
 
       ctx.stroke()
       ctx.setTransform(1, 0, 0, 1, 0, 0)
-    }
-  }
+    },
+  },
 }
diff --git a/app/src/experimental/Draggable.vue b/app/src/experimental/Draggable.vue
new file mode 100644
index 0000000000000000000000000000000000000000..add37934f91888d5280892efda3238c008b103f0
--- /dev/null
+++ b/app/src/experimental/Draggable.vue
@@ -0,0 +1,1018 @@
+<template>
+  <div
+    :style="style"
+    :class="[
+      {
+        [classNameActive]: enabled,
+        [classNameDragging]: dragging,
+        [classNameResizing]: resizing,
+        [classNameDraggable]: draggable,
+        [classNameResizable]: resizable,
+      },
+      className,
+    ]"
+    @mousedown="elementMouseDown"
+    @touchstart="elementTouchDown"
+  >
+    <div
+      v-for="handle in actualHandles"
+      :key="handle"
+      :class="[classNameHandle, classNameHandle + '-' + handle]"
+      :style="{
+        display: enabled ? 'block' : 'none',
+        transform: `scale(${1.0 / scale})`,
+      }"
+      @mousedown.stop.prevent="handleDown(handle, $event)"
+      @touchstart.stop.prevent="handleTouchDown(handle, $event)"
+    >
+      <slot :name="handle"></slot>
+    </div>
+    <slot></slot>
+  </div>
+</template>
+
+<script>
+import {
+  matchesSelectorToParentElements,
+  addEvent,
+  removeEvent,
+} from './utils/dom'
+
+const snapToGrid = (grid, pendingX, pendingY, scale = 1) => {
+  const [scaleX, scaleY] = typeof scale === 'number' ? [scale, scale] : scale
+  const x = Math.round(pendingX / scaleX / grid[0]) * grid[0]
+  const y = Math.round(pendingY / scaleY / grid[1]) * grid[1]
+  return [x, y]
+}
+
+// const getSize = (el) => {
+//   const rect = el.getBoundingClientRect()
+
+//   return [parseInt(rect.width), parseInt(rect.height)]
+// }
+
+const getComputedSize = (element) => {
+  const style = window.getComputedStyle(element)
+
+  return [
+    parseFloat(style.getPropertyValue('width'), 10),
+    parseFloat(style.getPropertyValue('height'), 10),
+  ]
+}
+
+const computeWidth = (parentWidth, left, right) => {
+  return parentWidth - left - right
+}
+
+const computeHeight = (parentHeight, top, bottom) => {
+  return parentHeight - top - bottom
+}
+
+const restrictToBounds = (value, min, max) => {
+  if (min !== null && value < min) {
+    return min
+  }
+
+  if (max !== null && max < value) {
+    return max
+  }
+
+  return value
+}
+const events = {
+  mouse: {
+    start: 'mousedown',
+    move: 'mousemove',
+    stop: 'mouseup',
+  },
+  touch: {
+    start: 'touchstart',
+    move: 'touchmove',
+    stop: 'touchend',
+  },
+}
+const userSelectNone = {
+  userSelect: 'none',
+  MozUserSelect: 'none',
+  WebkitUserSelect: 'none',
+  MsUserSelect: 'none',
+}
+const userSelectAuto = {
+  userSelect: 'auto',
+  MozUserSelect: 'auto',
+  WebkitUserSelect: 'auto',
+  MsUserSelect: 'auto',
+}
+let eventsFor = events.mouse
+
+export default {
+  props: {
+    className: {
+      type: String,
+      default: 'vdr',
+    },
+    classNameDraggable: {
+      type: String,
+      default: 'draggable',
+    },
+    classNameResizable: {
+      type: String,
+      default: 'resizable',
+    },
+    classNameDragging: {
+      type: String,
+      default: 'dragging',
+    },
+    classNameResizing: {
+      type: String,
+      default: 'resizing',
+    },
+    classNameActive: {
+      type: String,
+      default: 'active',
+    },
+    classNameHandle: {
+      type: String,
+      default: 'handle',
+    },
+    disableUserSelect: {
+      type: Boolean,
+      default: true,
+    },
+    enableNativeDrag: {
+      type: Boolean,
+      default: false,
+    },
+    preventDeactivation: {
+      type: Boolean,
+      default: false,
+    },
+    active: {
+      type: Boolean,
+      default: false,
+    },
+    draggable: {
+      type: Boolean,
+      default: true,
+    },
+    resizable: {
+      type: Boolean,
+      default: true,
+    },
+    lockAspectRatio: {
+      type: Boolean,
+      default: false,
+    },
+    w: {
+      type: [Number, String],
+      default: 200,
+      validator: (val) => {
+        if (typeof val === 'number') {
+          return val > 0
+        }
+        return val === 'auto'
+      },
+    },
+    h: {
+      type: [Number, String],
+      default: 200,
+      validator: (val) => {
+        if (typeof val === 'number') {
+          return val > 0
+        }
+        return val === 'auto'
+      },
+    },
+    minWidth: {
+      type: Number,
+      default: 0,
+      validator: (val) => val >= 0,
+    },
+    minHeight: {
+      type: Number,
+      default: 0,
+      validator: (val) => val >= 0,
+    },
+    maxWidth: {
+      type: Number,
+      default: null,
+      validator: (val) => val >= 0,
+    },
+    maxHeight: {
+      type: Number,
+      default: null,
+      validator: (val) => val >= 0,
+    },
+    x: {
+      type: Number,
+      default: 0,
+    },
+    y: {
+      type: Number,
+      default: 0,
+    },
+    z: {
+      type: [String, Number],
+      default: 'auto',
+      validator: (val) => (typeof val === 'string' ? val === 'auto' : val >= 0),
+    },
+    handles: {
+      type: Array,
+      default: () => ['tl', 'tm', 'tr', 'mr', 'br', 'bm', 'bl', 'ml'],
+      validator: (val) => {
+        const s = new Set(['tl', 'tm', 'tr', 'mr', 'br', 'bm', 'bl', 'ml'])
+        return new Set(val.filter((h) => s.has(h))).size === val.length
+      },
+    },
+    dragHandle: {
+      type: String,
+      default: null,
+    },
+    dragCancel: {
+      type: String,
+      default: null,
+    },
+    axis: {
+      type: String,
+      default: 'both',
+      validator: (val) => ['x', 'y', 'both'].includes(val),
+    },
+    grid: {
+      type: Array,
+      default: () => [1, 1],
+    },
+    parent: {
+      type: Boolean,
+      default: false,
+    },
+    scale: {
+      type: [Number, Array],
+      default: 1,
+      validator: (val) => {
+        if (typeof val === 'number') {
+          return val > 0
+        }
+        return val.length === 2 && val[0] > 0 && val[1] > 0
+      },
+    },
+    onDragStart: {
+      type: Function,
+      default: () => true,
+    },
+    onDrag: {
+      type: Function,
+      default: () => true,
+    },
+    onResizeStart: {
+      type: Function,
+      default: () => true,
+    },
+    onResize: {
+      type: Function,
+      default: () => true,
+    },
+  },
+  data: function () {
+    return {
+      left: this.x,
+      top: this.y,
+      right: null,
+      bottom: null,
+      width: null,
+      height: null,
+      widthTouched: false,
+      heightTouched: false,
+      aspectFactor: null,
+      parentWidth: null,
+      parentHeight: null,
+      minW: this.minWidth,
+      minH: this.minHeight,
+      maxW: this.maxWidth,
+      maxH: this.maxHeight,
+      handle: null,
+      enabled: this.active,
+      resizing: false,
+      dragging: false,
+      zIndex: this.z,
+    }
+  },
+  created: function () {
+    // eslint-disable-next-line
+    if (this.maxWidth && this.minWidth > this.maxWidth) console.warn('[Vdr warn]: Invalid prop: minWidth cannot be greater than maxWidth')
+    // eslint-disable-next-line
+    if (this.maxWidth && this.minHeight > this.maxHeight) console.warn('[Vdr warn]: Invalid prop: minHeight cannot be greater than maxHeight')
+    this.resetBoundsAndMouseState()
+  },
+  mounted: function () {
+    if (!this.enableNativeDrag) {
+      this.$el.ondragstart = () => false
+    }
+    const [parentWidth, parentHeight] = this.getParentSize()
+    this.parentWidth = parentWidth
+    this.parentHeight = parentHeight
+    const [width, height] = getComputedSize(this.$el)
+    this.aspectFactor =
+      (this.w !== 'auto' ? this.w : width) /
+      (this.h !== 'auto' ? this.h : height)
+    this.width = this.w !== 'auto' ? this.w : width
+    this.height = this.h !== 'auto' ? this.h : height
+    this.right = this.parentWidth - this.width - this.left
+    this.bottom = this.parentHeight - this.height - this.top
+    if (this.active) {
+      this.$emit('activated')
+    }
+    addEvent(document.documentElement, 'mousedown', this.deselect)
+    addEvent(document.documentElement, 'touchend touchcancel', this.deselect)
+    addEvent(window, 'resize', this.checkParentSize)
+  },
+  beforeDestroy: function () {
+    removeEvent(document.documentElement, 'mousedown', this.deselect)
+    removeEvent(document.documentElement, 'touchstart', this.handleUp)
+    removeEvent(document.documentElement, 'mousemove', this.move)
+    removeEvent(document.documentElement, 'touchmove', this.move)
+    removeEvent(document.documentElement, 'mouseup', this.handleUp)
+    removeEvent(document.documentElement, 'touchend touchcancel', this.deselect)
+    removeEvent(window, 'resize', this.checkParentSize)
+  },
+  methods: {
+    resetBoundsAndMouseState() {
+      this.mouseClickPosition = { mouseX: 0, mouseY: 0, x: 0, y: 0, w: 0, h: 0 }
+      this.bounds = {
+        minLeft: null,
+        maxLeft: null,
+        minRight: null,
+        maxRight: null,
+        minTop: null,
+        maxTop: null,
+        minBottom: null,
+        maxBottom: null,
+      }
+    },
+    checkParentSize() {
+      if (this.parent) {
+        const [newParentWidth, newParentHeight] = this.getParentSize()
+        this.parentWidth = newParentWidth
+        this.parentHeight = newParentHeight
+        this.right = this.parentWidth - this.width - this.left
+        this.bottom = this.parentHeight - this.height - this.top
+      }
+    },
+    getParentSize() {
+      if (this.parent) {
+        const style = window.getComputedStyle(this.$el.parentNode, null)
+        return [
+          parseInt(style.getPropertyValue('width'), 10),
+          parseInt(style.getPropertyValue('height'), 10),
+        ]
+      }
+      return [null, null]
+    },
+    elementTouchDown(e) {
+      eventsFor = events.touch
+      this.elementDown(e)
+    },
+    elementMouseDown(e) {
+      eventsFor = events.mouse
+      this.elementDown(e)
+    },
+    elementDown(e) {
+      if (e instanceof MouseEvent && e.which !== 1) {
+        return
+      }
+      const target = e.target || e.srcElement
+      if (this.$el.contains(target)) {
+        if (this.onDragStart(e) === false) {
+          return
+        }
+        if (
+          (this.dragHandle &&
+            !matchesSelectorToParentElements(
+              target,
+              this.dragHandle,
+              this.$el
+            )) ||
+          (this.dragCancel &&
+            matchesSelectorToParentElements(target, this.dragCancel, this.$el))
+        ) {
+          this.dragging = false
+          return
+        }
+        if (!this.enabled) {
+          this.enabled = true
+          this.$emit('activated')
+          this.$emit('update:active', true)
+        }
+        if (this.draggable) {
+          this.dragging = true
+        }
+        this.mouseClickPosition.mouseX = e.touches
+          ? e.touches[0].pageX
+          : e.pageX
+        this.mouseClickPosition.mouseY = e.touches
+          ? e.touches[0].pageY
+          : e.pageY
+        this.mouseClickPosition.left = this.left
+        this.mouseClickPosition.right = this.right
+        this.mouseClickPosition.top = this.top
+        this.mouseClickPosition.bottom = this.bottom
+        if (this.parent) {
+          this.bounds = this.calcDragLimits()
+        }
+        addEvent(document.documentElement, eventsFor.move, this.move)
+        addEvent(document.documentElement, eventsFor.stop, this.handleUp)
+      }
+    },
+    calcDragLimits() {
+      return {
+        minLeft: this.left % this.grid[0],
+        maxLeft:
+          Math.floor(
+            (this.parentWidth - this.width - this.left) / this.grid[0]
+          ) *
+            this.grid[0] +
+          this.left,
+        minRight: this.right % this.grid[0],
+        maxRight:
+          Math.floor(
+            (this.parentWidth - this.width - this.right) / this.grid[0]
+          ) *
+            this.grid[0] +
+          this.right,
+        minTop: this.top % this.grid[1],
+        maxTop:
+          Math.floor(
+            (this.parentHeight - this.height - this.top) / this.grid[1]
+          ) *
+            this.grid[1] +
+          this.top,
+        minBottom: this.bottom % this.grid[1],
+        maxBottom:
+          Math.floor(
+            (this.parentHeight - this.height - this.bottom) / this.grid[1]
+          ) *
+            this.grid[1] +
+          this.bottom,
+      }
+    },
+    deselect(e) {
+      const target = e.target || e.srcElement
+      const regex = new RegExp(this.className + '-([trmbl]{2})', '')
+      if (!this.$el.contains(target) && !regex.test(target.className)) {
+        if (this.enabled && !this.preventDeactivation) {
+          this.enabled = false
+          this.$emit('deactivated')
+          this.$emit('update:active', false)
+        }
+        removeEvent(document.documentElement, eventsFor.move, this.handleResize)
+      }
+      this.resetBoundsAndMouseState()
+    },
+    handleTouchDown(handle, e) {
+      eventsFor = events.touch
+      this.handleDown(handle, e)
+    },
+    handleDown(handle, e) {
+      if (e instanceof MouseEvent && e.which !== 1) {
+        return
+      }
+      if (this.onResizeStart(handle, e) === false) {
+        return
+      }
+      if (e.stopPropagation) e.stopPropagation()
+      // Here we avoid a dangerous recursion by faking
+      // corner handles as middle handles
+      if (this.lockAspectRatio && !handle.includes('m')) {
+        this.handle = 'm' + handle.substring(1)
+      } else {
+        this.handle = handle
+      }
+      this.resizing = true
+      this.mouseClickPosition.mouseX = e.touches ? e.touches[0].pageX : e.pageX
+      this.mouseClickPosition.mouseY = e.touches ? e.touches[0].pageY : e.pageY
+      this.mouseClickPosition.left = this.left
+      this.mouseClickPosition.right = this.right
+      this.mouseClickPosition.top = this.top
+      this.mouseClickPosition.bottom = this.bottom
+      this.bounds = this.calcResizeLimits()
+      addEvent(document.documentElement, eventsFor.move, this.handleResize)
+      addEvent(document.documentElement, eventsFor.stop, this.handleUp)
+    },
+    calcResizeLimits() {
+      let minW = this.minW
+      let minH = this.minH
+      let maxW = this.maxW
+      let maxH = this.maxH
+      const aspectFactor = this.aspectFactor
+      const [gridX, gridY] = this.grid
+      const width = this.width
+      const height = this.height
+      const left = this.left
+      const top = this.top
+      const right = this.right
+      const bottom = this.bottom
+      if (this.lockAspectRatio) {
+        if (minW / minH > aspectFactor) {
+          minH = minW / aspectFactor
+        } else {
+          minW = aspectFactor * minH
+        }
+        if (maxW && maxH) {
+          maxW = Math.min(maxW, aspectFactor * maxH)
+          maxH = Math.min(maxH, maxW / aspectFactor)
+        } else if (maxW) {
+          maxH = maxW / aspectFactor
+        } else if (maxH) {
+          maxW = aspectFactor * maxH
+        }
+      }
+      maxW = maxW - (maxW % gridX)
+      maxH = maxH - (maxH % gridY)
+      const limits = {
+        minLeft: null,
+        maxLeft: null,
+        minTop: null,
+        maxTop: null,
+        minRight: null,
+        maxRight: null,
+        minBottom: null,
+        maxBottom: null,
+      }
+      if (this.parent) {
+        limits.minLeft = left % gridX
+        limits.maxLeft = left + Math.floor((width - minW) / gridX) * gridX
+        limits.minTop = top % gridY
+        limits.maxTop = top + Math.floor((height - minH) / gridY) * gridY
+        limits.minRight = right % gridX
+        limits.maxRight = right + Math.floor((width - minW) / gridX) * gridX
+        limits.minBottom = bottom % gridY
+        limits.maxBottom = bottom + Math.floor((height - minH) / gridY) * gridY
+        if (maxW) {
+          limits.minLeft = Math.max(
+            limits.minLeft,
+            this.parentWidth - right - maxW
+          )
+          limits.minRight = Math.max(
+            limits.minRight,
+            this.parentWidth - left - maxW
+          )
+        }
+        if (maxH) {
+          limits.minTop = Math.max(
+            limits.minTop,
+            this.parentHeight - bottom - maxH
+          )
+          limits.minBottom = Math.max(
+            limits.minBottom,
+            this.parentHeight - top - maxH
+          )
+        }
+        if (this.lockAspectRatio) {
+          limits.minLeft = Math.max(limits.minLeft, left - top * aspectFactor)
+          limits.minTop = Math.max(limits.minTop, top - left / aspectFactor)
+          limits.minRight = Math.max(
+            limits.minRight,
+            right - bottom * aspectFactor
+          )
+          limits.minBottom = Math.max(
+            limits.minBottom,
+            bottom - right / aspectFactor
+          )
+        }
+      } else {
+        limits.minLeft = null
+        limits.maxLeft = left + Math.floor((width - minW) / gridX) * gridX
+        limits.minTop = null
+        limits.maxTop = top + Math.floor((height - minH) / gridY) * gridY
+        limits.minRight = null
+        limits.maxRight = right + Math.floor((width - minW) / gridX) * gridX
+        limits.minBottom = null
+        limits.maxBottom = bottom + Math.floor((height - minH) / gridY) * gridY
+        if (maxW) {
+          limits.minLeft = -(right + maxW)
+          limits.minRight = -(left + maxW)
+        }
+        if (maxH) {
+          limits.minTop = -(bottom + maxH)
+          limits.minBottom = -(top + maxH)
+        }
+        if (this.lockAspectRatio && maxW && maxH) {
+          limits.minLeft = Math.min(limits.minLeft, -(right + maxW))
+          limits.minTop = Math.min(limits.minTop, -(maxH + bottom))
+          limits.minRight = Math.min(limits.minRight, -left - maxW)
+          limits.minBottom = Math.min(limits.minBottom, -top - maxH)
+        }
+      }
+      return limits
+    },
+    move(e) {
+      if (this.resizing) {
+        this.handleResize(e)
+      } else if (this.dragging) {
+        this.handleDrag(e)
+      }
+    },
+    handleDrag(e) {
+      const axis = this.axis
+      const grid = this.grid
+      const bounds = this.bounds
+      const mouseClickPosition = this.mouseClickPosition
+      const tmpDeltaX =
+        axis && axis !== 'y'
+          ? mouseClickPosition.mouseX -
+            (e.touches ? e.touches[0].pageX : e.pageX)
+          : 0
+      const tmpDeltaY =
+        axis && axis !== 'x'
+          ? mouseClickPosition.mouseY -
+            (e.touches ? e.touches[0].pageY : e.pageY)
+          : 0
+      const [deltaX, deltaY] = snapToGrid(
+        grid,
+        tmpDeltaX,
+        tmpDeltaY,
+        this.scale
+      )
+      const left = restrictToBounds(
+        mouseClickPosition.left - deltaX,
+        bounds.minLeft,
+        bounds.maxLeft
+      )
+      const top = restrictToBounds(
+        mouseClickPosition.top - deltaY,
+        bounds.minTop,
+        bounds.maxTop
+      )
+      if (this.onDrag(left, top) === false) {
+        return
+      }
+      const right = restrictToBounds(
+        mouseClickPosition.right + deltaX,
+        bounds.minRight,
+        bounds.maxRight
+      )
+      const bottom = restrictToBounds(
+        mouseClickPosition.bottom + deltaY,
+        bounds.minBottom,
+        bounds.maxBottom
+      )
+      this.left = left
+      this.top = top
+      this.right = right
+      this.bottom = bottom
+      this.$emit('dragging', this.left, this.top)
+    },
+    moveHorizontally(val) {
+      // eslint-disable-next-line
+      const [deltaX, _] = snapToGrid(this.grid, val, this.top, 1)
+      const left = restrictToBounds(
+        deltaX,
+        this.bounds.minLeft,
+        this.bounds.maxLeft
+      )
+      this.left = left
+      this.right = this.parentWidth - this.width - left
+    },
+    moveVertically(val) {
+      // eslint-disable-next-line
+      const [_, deltaY] = snapToGrid(this.grid, this.left, val, 1)
+      const top = restrictToBounds(
+        deltaY,
+        this.bounds.minTop,
+        this.bounds.maxTop
+      )
+      this.top = top
+      this.bottom = this.parentHeight - this.height - top
+    },
+    handleResize(e) {
+      let left = this.left
+      let top = this.top
+      let right = this.right
+      let bottom = this.bottom
+      const mouseClickPosition = this.mouseClickPosition
+      const aspectFactor = this.aspectFactor
+      const tmpDeltaX =
+        mouseClickPosition.mouseX - (e.touches ? e.touches[0].pageX : e.pageX)
+      const tmpDeltaY =
+        mouseClickPosition.mouseY - (e.touches ? e.touches[0].pageY : e.pageY)
+      if (!this.widthTouched && tmpDeltaX) {
+        this.widthTouched = true
+      }
+      if (!this.heightTouched && tmpDeltaY) {
+        this.heightTouched = true
+      }
+      const [deltaX, deltaY] = snapToGrid(
+        this.grid,
+        tmpDeltaX,
+        tmpDeltaY,
+        this.scale
+      )
+      if (this.handle.includes('b')) {
+        bottom = restrictToBounds(
+          mouseClickPosition.bottom + deltaY,
+          this.bounds.minBottom,
+          this.bounds.maxBottom
+        )
+        if (this.lockAspectRatio && this.resizingOnY) {
+          right = this.right - (this.bottom - bottom) * aspectFactor
+        }
+      } else if (this.handle.includes('t')) {
+        top = restrictToBounds(
+          mouseClickPosition.top - deltaY,
+          this.bounds.minTop,
+          this.bounds.maxTop
+        )
+        if (this.lockAspectRatio && this.resizingOnY) {
+          left = this.left - (this.top - top) * aspectFactor
+        }
+      }
+      if (this.handle.includes('r')) {
+        right = restrictToBounds(
+          mouseClickPosition.right + deltaX,
+          this.bounds.minRight,
+          this.bounds.maxRight
+        )
+        if (this.lockAspectRatio && this.resizingOnX) {
+          bottom = this.bottom - (this.right - right) / aspectFactor
+        }
+      } else if (this.handle.includes('l')) {
+        left = restrictToBounds(
+          mouseClickPosition.left - deltaX,
+          this.bounds.minLeft,
+          this.bounds.maxLeft
+        )
+        if (this.lockAspectRatio && this.resizingOnX) {
+          top = this.top - (this.left - left) / aspectFactor
+        }
+      }
+      const width = computeWidth(this.parentWidth, left, right)
+      const height = computeHeight(this.parentHeight, top, bottom)
+      if (this.onResize(this.handle, left, top, width, height) === false) {
+        return
+      }
+      this.left = left
+      this.top = top
+      this.right = right
+      this.bottom = bottom
+      this.width = width
+      this.height = height
+      this.$emit('resizing', this.left, this.top, this.width, this.height)
+    },
+    changeWidth(val) {
+      // should calculate with scale 1.
+      // eslint-disable-next-line
+      const [newWidth, _] = snapToGrid(this.grid, val, 0, 1)
+      let right = restrictToBounds(
+        this.parentWidth - newWidth - this.left,
+        this.bounds.minRight,
+        this.bounds.maxRight
+      )
+      let bottom = this.bottom
+      if (this.lockAspectRatio) {
+        bottom = this.bottom - (this.right - right) / this.aspectFactor
+      }
+      const width = computeWidth(this.parentWidth, this.left, right)
+      const height = computeHeight(this.parentHeight, this.top, bottom)
+      this.right = right
+      this.bottom = bottom
+      this.width = width
+      this.height = height
+    },
+    changeHeight(val) {
+      // should calculate with scale 1.
+      // eslint-disable-next-line
+      const [_, newHeight] = snapToGrid(this.grid, 0, val, 1)
+      let bottom = restrictToBounds(
+        this.parentHeight - newHeight - this.top,
+        this.bounds.minBottom,
+        this.bounds.maxBottom
+      )
+      let right = this.right
+      if (this.lockAspectRatio) {
+        right = this.right - (this.bottom - bottom) * this.aspectFactor
+      }
+      const width = computeWidth(this.parentWidth, this.left, right)
+      const height = computeHeight(this.parentHeight, this.top, bottom)
+      this.right = right
+      this.bottom = bottom
+      this.width = width
+      this.height = height
+    },
+    handleUp() {
+      this.handle = null
+      this.resetBoundsAndMouseState()
+      if (this.resizing) {
+        this.resizing = false
+        this.$emit('resizestop', this.left, this.top, this.width, this.height)
+      }
+      if (this.dragging) {
+        this.dragging = false
+        this.$emit('dragstop', this.left, this.top)
+      }
+      removeEvent(document.documentElement, eventsFor.move, this.handleResize)
+    },
+  },
+  computed: {
+    style() {
+      return {
+        transform: `translate(${this.left}px, ${this.top}px)`,
+        width: this.computedWidth,
+        height: this.computedHeight,
+        zIndex: this.zIndex,
+        ...(this.dragging && this.disableUserSelect
+          ? userSelectNone
+          : userSelectAuto),
+      }
+    },
+    actualHandles() {
+      if (!this.resizable) return []
+      return this.handles
+    },
+    computedWidth() {
+      if (this.w === 'auto') {
+        if (!this.widthTouched) {
+          return 'auto'
+        }
+      }
+      return this.width + 'px'
+    },
+    computedHeight() {
+      if (this.h === 'auto') {
+        if (!this.heightTouched) {
+          return 'auto'
+        }
+      }
+      return this.height + 'px'
+    },
+    resizingOnX() {
+      return (
+        Boolean(this.handle) &&
+        (this.handle.includes('l') || this.handle.includes('r'))
+      )
+    },
+    resizingOnY() {
+      return (
+        Boolean(this.handle) &&
+        (this.handle.includes('t') || this.handle.includes('b'))
+      )
+    },
+    isCornerHandle() {
+      return (
+        Boolean(this.handle) && ['tl', 'tr', 'br', 'bl'].includes(this.handle)
+      )
+    },
+  },
+  watch: {
+    active(val) {
+      this.enabled = val
+      if (val) {
+        this.$emit('activated')
+      } else {
+        this.$emit('deactivated')
+      }
+    },
+    z(val) {
+      if (val >= 0 || val === 'auto') {
+        this.zIndex = val
+      }
+    },
+    x(val) {
+      if (this.resizing || this.dragging) {
+        return
+      }
+      if (this.parent) {
+        this.bounds = this.calcDragLimits()
+      }
+      this.moveHorizontally(val)
+    },
+    y(val) {
+      if (this.resizing || this.dragging) {
+        return
+      }
+      if (this.parent) {
+        this.bounds = this.calcDragLimits()
+      }
+      this.moveVertically(val)
+    },
+    lockAspectRatio(val) {
+      if (val) {
+        this.aspectFactor = this.width / this.height
+      } else {
+        this.aspectFactor = undefined
+      }
+    },
+    minWidth(val) {
+      if (val > 0 && val <= this.width) {
+        this.minW = val
+      }
+    },
+    minHeight(val) {
+      if (val > 0 && val <= this.height) {
+        this.minH = val
+      }
+    },
+    maxWidth(val) {
+      this.maxW = val
+    },
+    maxHeight(val) {
+      this.maxH = val
+    },
+    w(val) {
+      if (this.resizing || this.dragging) {
+        return
+      }
+      if (this.parent) {
+        this.bounds = this.calcResizeLimits()
+      }
+      this.changeWidth(val)
+    },
+    h(val) {
+      if (this.resizing || this.dragging) {
+        return
+      }
+      if (this.parent) {
+        this.bounds = this.calcResizeLimits()
+      }
+      this.changeHeight(val)
+    },
+  },
+}
+</script>
+
+<style scoped>
+.vdr {
+  touch-action: none;
+  position: absolute;
+  box-sizing: border-box;
+  border: 1px dashed black;
+}
+.handle {
+  box-sizing: border-box;
+  position: absolute;
+  width: 10px;
+  height: 10px;
+  background: #eee;
+  border: 1px solid #333;
+}
+.handle-tl {
+  top: -10px;
+  left: -10px;
+  cursor: nw-resize;
+  transform-origin: 100% 100%;
+}
+.handle-tm {
+  top: -10px;
+  left: 50%;
+  margin-left: -5px;
+  cursor: n-resize;
+  transform-origin: 50% 100%;
+}
+.handle-tr {
+  top: -10px;
+  right: -10px;
+  cursor: ne-resize;
+  transform-origin: 0% 100%;
+}
+.handle-ml {
+  top: 50%;
+  margin-top: -5px;
+  left: -10px;
+  cursor: w-resize;
+  transform-origin: 100% 50%;
+}
+.handle-mr {
+  top: 50%;
+  margin-top: -5px;
+  right: -10px;
+  cursor: e-resize;
+  transform-origin: 0% 50%;
+}
+.handle-bl {
+  bottom: -10px;
+  left: -10px;
+  cursor: sw-resize;
+  transform-origin: 100% 0%;
+}
+.handle-bm {
+  bottom: -10px;
+  left: 50%;
+  margin-left: -5px;
+  cursor: s-resize;
+  transform-origin: 50% 0%;
+}
+.handle-br {
+  bottom: -10px;
+  right: -10px;
+  cursor: se-resize;
+  transform-origin: 0% 0%;
+}
+@media only screen and (max-width: 768px) {
+  [class*='handle-']:before {
+    content: '';
+    left: -10px;
+    right: -10px;
+    bottom: -10px;
+    top: -10px;
+    position: absolute;
+  }
+}
+</style>
diff --git a/app/src/experimental/PanZoomContainer.vue b/app/src/experimental/PanZoomContainer.vue
index 2b0d317ca91f85851459df3cca88f21f0c8e53e3..b32853c47bd7d7a153438a1e43d9457119b11c7b 100644
--- a/app/src/experimental/PanZoomContainer.vue
+++ b/app/src/experimental/PanZoomContainer.vue
@@ -102,14 +102,6 @@ export default {
         e.preventDefault()
         this.shouldPreventTouchEndDefault = false
       }
-
-      // const { relativePoint, boardPoint } = getNormalisedInteraction(
-      //   this.$refs.container,
-      //   e,
-      //   this.translation,
-      //   this.scale
-      // )
-      // console.log(relativePoint, boardPoint)
     },
     reset() {},
     onMouseDown(e) {
diff --git a/app/src/experimental/constants/color.js b/app/src/experimental/constants/color.js
index f40ba960dc85187788d7e2503fe96c211554145d..ed9dee49d97538ccc1653c3b2650ef8e9487565a 100644
--- a/app/src/experimental/constants/color.js
+++ b/app/src/experimental/constants/color.js
@@ -1,36 +1,36 @@
 export const palette = {
   coral: {
     light: '#FFD8E1',
-    dark: '#F56789'
+    dark: '#F56789',
   },
   lime: {
     light: '#EEFFBC',
-    dark: '#B9DF4E'
+    dark: '#B9DF4E',
   },
   blue: {
     light: '#CDEAFF',
-    dark: '#4EB4FF'
+    dark: '#4EB4FF',
   },
   orange: {
     light: '#FFD8CC',
-    dark: '#FF8762'
+    dark: '#FF8762',
   },
   purple: {
     light: '#DAD9FE',
-    dark: '#8A80F6'
+    dark: '#8A80F6',
   },
   pink: {
     light: '#FBE9FF',
-    dark: '#E47EFD'
+    dark: '#E47EFD',
   },
   yellow: {
     light: '#FFF3CA',
-    dark: '#FFD84F'
+    dark: '#FFD84F',
   },
   mono: {
     light: '#FFFFFF',
-    dark: '#A3A3A3'
-  }
+    dark: '#A3A3A3',
+  },
 }
 
 /**
@@ -47,8 +47,8 @@ export const getPalette = (color = 'mono', type) =>
 
 export const paletteArray = () => {
   const result = []
-  Object.keys(palette).forEach(p => {
-    Object.keys(palette[p]).forEach(h => {
+  Object.keys(palette).forEach((p) => {
+    Object.keys(palette[p]).forEach((h) => {
       result.push({ name: `${p}-${h}`, value: palette[p][h] })
     })
   })
diff --git a/app/src/experimental/constants/sizes.js b/app/src/experimental/constants/sizes.js
index 38422e80138cedf5594eff459481d4c86edcc266..bc59f0b33f0e5500acd3ef1ea59b9d2805af9e85 100644
--- a/app/src/experimental/constants/sizes.js
+++ b/app/src/experimental/constants/sizes.js
@@ -1,15 +1,15 @@
 export const nodeSizes = {
   min: {
     width: 100,
-    height: 100
+    height: 100,
   },
   max: {
     width: 600,
-    height: 600
-  }
+    height: 600,
+  },
 }
 
 export const defaultNode = {
   width: 200,
-  height: 150
+  height: 150,
 }
diff --git a/app/src/experimental/layers/ConnectionsLayer.vue b/app/src/experimental/layers/ConnectionsLayer.vue
index 27c814260ea41d596475afa401c5b9270711a046..1d2b233975f000d2fa871c7ac5d05093f0b6ece1 100644
--- a/app/src/experimental/layers/ConnectionsLayer.vue
+++ b/app/src/experimental/layers/ConnectionsLayer.vue
@@ -1,4 +1,4 @@
-  <template>
+<template>
   <svg
     v-bind:viewBox="`0 0 ${width} ${height}`"
     :width="width"
diff --git a/app/src/experimental/layers/SelectionLayer.vue b/app/src/experimental/layers/SelectionLayer.vue
index 992cf29f41cc0a3dd224ea3390d29032bc50fcf2..eee59948524ce3f37cd7241e4dcec904a18c542b 100644
--- a/app/src/experimental/layers/SelectionLayer.vue
+++ b/app/src/experimental/layers/SelectionLayer.vue
@@ -10,14 +10,14 @@ import { clear } from '@/experimental/utils/canvas'
 export default {
   props: {
     width: {
-      type: Number
+      type: Number,
     },
     height: {
-      type: Number
+      type: Number,
     },
     shape: {
-      type: Object
-    }
+      type: Object,
+    },
   },
   mounted() {
     this.canvas = this.$refs.canvas
@@ -28,7 +28,7 @@ export default {
     return {
       canvas: {},
       context: {},
-      color: palette.blue.dark
+      color: palette.blue.dark,
     }
   },
   // Watch the props for changes and, if necessary, redraw canvas
@@ -44,7 +44,7 @@ export default {
     },
     selected() {
       this.draw()
-    }
+    },
   },
   methods: {
     /**
@@ -69,8 +69,8 @@ export default {
 
         this.context.setTransform(1, 0, 0, 1, 0, 0)
       }
-    }
-  }
+    },
+  },
 }
 </script>
 <style scoped>
diff --git a/app/src/experimental/modes/select.js b/app/src/experimental/modes/select.js
index 846897c7e5e63598b0fd767877d2908bfd00f4f5..2440acf9159e891236c8f2dbca89b99a6539e536 100644
--- a/app/src/experimental/modes/select.js
+++ b/app/src/experimental/modes/select.js
@@ -1,3 +1,3 @@
 export default {
-  mixins: {}
+  mixins: {},
 }
diff --git a/app/src/experimental/uiText.js b/app/src/experimental/uiText.js
index d0008e2c516b7212fb9692aa5ce77e70b9af89ba..cc628465f8578689022dd396e686fc90f0c0b597 100644
--- a/app/src/experimental/uiText.js
+++ b/app/src/experimental/uiText.js
@@ -3,20 +3,20 @@ export const uiText = {
     modes: {
       move: {
         title: 'Move',
-        description: 'Move your view of the board'
+        description: 'Move your view of the board',
       },
       select: {
         title: 'Select',
-        description: 'Select and edit items on the board'
+        description: 'Select and edit items on the board',
       },
       connect: {
         title: 'Connect',
-        description: 'Make connections between the nodes'
+        description: 'Make connections between the nodes',
       },
       draw: {
         title: 'Draw',
-        description: 'Sketch and annotate around your nodes'
-      }
-    }
-  }
+        description: 'Sketch and annotate around your nodes',
+      },
+    },
+  },
 }
diff --git a/app/src/experimental/utils/canvas.js b/app/src/experimental/utils/canvas.js
index 89de73a01e54a03e50433565ff01cf01103526e8..f3f713c79782b9c633f147be8c24640f2022950e 100644
--- a/app/src/experimental/utils/canvas.js
+++ b/app/src/experimental/utils/canvas.js
@@ -3,7 +3,7 @@ import { generateLinkHandles } from '@/experimental/utils/nodes'
 import {
   mapRange,
   distanceBetween,
-  angleBetween
+  angleBetween,
 } from '@/experimental/utils/numbers'
 
 //////////////////////////////////////////////////////////////////////
diff --git a/app/src/experimental/utils/dom.js b/app/src/experimental/utils/dom.js
index 57cdb7e43efdefdcb442980f2e350aa05eced4c2..4055c73b9c3786e1458255227e27c8fb59a7de83 100644
--- a/app/src/experimental/utils/dom.js
+++ b/app/src/experimental/utils/dom.js
@@ -23,7 +23,7 @@ export const getInteractionPoint = (target, event) => {
     y: parseInt(
       ((event.clientY - rect.top) / (rect.bottom - rect.top)) *
         target.offsetHeight
-    )
+    ),
   }
 }
 
@@ -32,13 +32,13 @@ const matches = [
   'webkitMatchesSelector',
   'mozMatchesSelector',
   'msMatchesSelector',
-  'oMatchesSelector'
+  'oMatchesSelector',
 ]
 
 export const matchesSelectorToParentElements = (el, selector, baseNode) => {
   let node = el
 
-  const matchesSelectorFunc = matches.find(func => isFunction(node[func]))
+  const matchesSelectorFunc = matches.find((func) => isFunction(node[func]))
 
   if (!isFunction(node[matchesSelectorFunc])) return false
 
diff --git a/app/src/experimental/utils/helpers.js b/app/src/experimental/utils/helpers.js
index 9f5898ddce3da7329a2471a1a3453965e6b05da4..f61b76c4088bbeb50b638999b8084bf9d095edc3 100644
--- a/app/src/experimental/utils/helpers.js
+++ b/app/src/experimental/utils/helpers.js
@@ -33,7 +33,7 @@ export const shallowRemoveFromArray = (arr, item) => {
  * Utility method to group array of objects by key value
  * from https://gist.github.com/JamieMason/0566f8412af9fe6a1d470aa1e089a752
  */
-export const groupBy = key => array =>
+export const groupBy = (key) => (array) =>
   array.reduce((objectsByKeyValue, obj) => {
     const value = obj[key]
     objectsByKeyValue[value] = (objectsByKeyValue[value] || []).concat(obj)
diff --git a/app/src/experimental/utils/nodes.js b/app/src/experimental/utils/nodes.js
index 8f2b3470487dec26365b6af49fc315b93bf4da4a..90d82bc0a537a2fb5afdf0fab089c25de796593f 100644
--- a/app/src/experimental/utils/nodes.js
+++ b/app/src/experimental/utils/nodes.js
@@ -45,7 +45,7 @@ export const nodeToArea = (node, threshold = 0.0) => {
     x1: node.x * (1.0 - threshold),
     y1: node.y * (1.0 - threshold),
     x2: (node.x + node.width) * (1.0 + threshold),
-    y2: (node.y + node.height) * (1.0 + threshold)
+    y2: (node.y + node.height) * (1.0 + threshold),
   }
 }
 
@@ -56,12 +56,12 @@ export const nodeToArea = (node, threshold = 0.0) => {
  * @return {Node}
  *
  *  */
-export const areaToNode = area => {
+export const areaToNode = (area) => {
   return {
     x: area.x1,
     y: area.y1,
     width: Math.abs(area.x2 - area.x1),
-    height: Math.abs(area.y2 - area.y1)
+    height: Math.abs(area.y2 - area.y1),
   }
 }
 
@@ -104,13 +104,13 @@ export const generateLinkHandles = (from, to) => {
   return [
     {
       x: from.x + from.width,
-      y: from.y + from.height / 2
+      y: from.y + from.height / 2,
     },
     {
       // x: to.x + to.width / 2,
       x: to.x + 10,
-      y: to.y + to.height / 2
-    }
+      y: to.y + to.height / 2,
+    },
   ]
 }
 
@@ -125,7 +125,7 @@ export const generateLinkHandles = (from, to) => {
  *  */
 export const areaNodeIntersections = (targetNode, nodes, threshold = 0) => {
   return nodes
-    .filter(node =>
+    .filter((node) =>
       areaOverlaps(nodeToArea(targetNode), nodeToArea(node, threshold))
     )
     .map(({ id }) => id)
@@ -140,7 +140,9 @@ export const areaNodeIntersections = (targetNode, nodes, threshold = 0) => {
  *
  *  */
 export const pointNodeIntersections = (point, nodes) => {
-  return nodes.filter(node => pointWithinNode(point, node)).map(({ id }) => id)
+  return nodes
+    .filter((node) => pointWithinNode(point, node))
+    .map(({ id }) => id)
 }
 
 /**
@@ -163,7 +165,7 @@ export const pointWithinNode = (point, node) => {
  * @return {Area}
  *
  *  */
-export const generateAreaFromNodes = nodes => {
+export const generateAreaFromNodes = (nodes) => {
   const sum = {}
 
   for (let node of nodes) {
@@ -201,7 +203,7 @@ export const generateNode = (origin, target) => {
     x: minusX ? origin.x - width : origin.x,
     y: minusY ? origin.y - height : origin.y,
     width,
-    height
+    height,
   }
 }
 
@@ -225,15 +227,15 @@ export const transformNodeSelection = (nodes, transform) => {
     }
   }
 
-  return nodes.map(node => {
+  return nodes.map((node) => {
     const relativeTransform = {
       x: node.x - baseCoordinates.x,
-      y: node.y - baseCoordinates.y
+      y: node.y - baseCoordinates.y,
     }
 
     return Object.assign({}, node, {
       x: transform.x + relativeTransform.x,
-      y: transform.y + relativeTransform.y
+      y: transform.y + relativeTransform.y,
     })
   })
 }
diff --git a/app/src/experimental/utils/numbers.js b/app/src/experimental/utils/numbers.js
index 2ab8204ff22de968e83bf5007eb978554196fe80..fef04c3fe4b41c6f3b0672469d536c4474ed6617 100644
--- a/app/src/experimental/utils/numbers.js
+++ b/app/src/experimental/utils/numbers.js
@@ -35,7 +35,7 @@ export const lerp = (from, to, extent) => from * (1 - extent) + to * extent
 export const lerpPoint = (from, to, extent) => {
   return {
     x: lerp(from.x, to.x, extent),
-    y: lerp(from.y, to.y, extent)
+    y: lerp(from.y, to.y, extent),
   }
 }
 
@@ -92,7 +92,7 @@ export const scaleCoordinate = (coordinate, scaleRatio) => {
 export const scalePoint = ({ x, y }, scale) => {
   return {
     x: scaleCoordinate(x, scale),
-    y: scaleCoordinate(y, scale)
+    y: scaleCoordinate(y, scale),
   }
 }
 
@@ -108,6 +108,6 @@ export const constrainTranslation = (targetTranslation, translationBounds) => {
   yMax = yMax != undefined ? yMax : Infinity
   return {
     x: clamp(x, xMin, xMax),
-    y: clamp(y, yMin, yMax)
+    y: clamp(y, yMin, yMax),
   }
 }
diff --git a/app/src/experimental/utils/parse.js b/app/src/experimental/utils/parse.js
index 02b2e3bd145ad228954235ed4df8473cb00779a0..8e29954f1260a47b737f89ef628354a45be9605b 100644
--- a/app/src/experimental/utils/parse.js
+++ b/app/src/experimental/utils/parse.js
@@ -1,30 +1,30 @@
-export const parseHex = hexColor => {
+export const parseHex = (hexColor) => {
   const parser = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hexColor)
   if (parser) {
     return [
       parseInt(parser[1], 16) / 255.0,
       parseInt(parser[2], 16) / 255.0,
-      parseInt(parser[3], 16) / 255.0
+      parseInt(parser[3], 16) / 255.0,
     ]
   } else {
     throw new Error(`${hexColor} is not a valid hex color code`)
   }
 }
 
-export const parseNumberToHex = num => {
+export const parseNumberToHex = (num) => {
   const c = num * 255.0
   const hex = c.toString(16)
   return hex.length === 1 ? '0' + hex : hex
 }
 
 // derived from https://github.com/mrdoob/three.js/blob/dev/src/math/Color.js
-export const parseRGBString = str => {
+export const parseRGBString = (str) => {
   /* eslint-disable no-useless-escape */
   const components = /^((?:rgb|hsl)a?)\(\s*([^\)]*)\)/.exec(str)
   const result = components[2].split(',')
 
   if (result.length === 3 || result.length === 4) {
-    return result.map(n => parseFloat(n) / 255)
+    return result.map((n) => parseFloat(n) / 255)
   } else {
     return new Error(`Invalid color string ${str}`)
   }
diff --git a/app/src/store/index.js b/app/src/store/index.js
index 05a22ee2e2c08cd3b046edb67a30fb153f1cd4df..ab652eb207ad2ed5ca9df9a0bf06529bdff1d15c 100644
--- a/app/src/store/index.js
+++ b/app/src/store/index.js
@@ -1,14 +1,12 @@
 import Vue from 'vue'
 import Vuex from 'vuex'
 import PouchDB from 'pouchdb'
-import VueDraggableResizable from 'vue-draggable-resizable'
-import 'vue-draggable-resizable/dist/VueDraggableResizable.css'
 
 import uiStore from '@/experimental/uiStore'
 import Router from '@/router'
 
 Vue.use(Vuex)
-Vue.component('vue-draggable-resizable', VueDraggableResizable)
+
 var myclient = 'firstvisit'
 var localxpos = 50
 var localypos = 50
diff --git a/app/src/views/Cards.vue b/app/src/views/Cards.vue
index 567e980a66ffe161b1f0ae1173999c09f7e67d5b..acb3372b973ddd30444224cf54d0ad503624de07 100644
--- a/app/src/views/Cards.vue
+++ b/app/src/views/Cards.vue
@@ -176,8 +176,6 @@ export default {
   cursor: pointer;
 }
 
-
-
 .a {
   fill: #333;
   stroke: #707070;
diff --git a/app/vue.config.js b/app/vue.config.js
index b377ce6c426742fc1780ab67f87c313afb425c6e..6ca6c2175d64b0b8e645badccdbef0e377f805b5 100644
--- a/app/vue.config.js
+++ b/app/vue.config.js
@@ -10,12 +10,12 @@ module.exports = {
             {
               loader: 'file-loader',
               options: {
-                name: 'files/[name].[ext]'
-              }
-            }
-          ]
-        }
-      ]
-    }
-  }
+                name: 'files/[name].[ext]',
+              },
+            },
+          ],
+        },
+      ],
+    },
+  },
 }