Visual Window Snapping

Okay, I’m into what you’ve got there. I’m gonna take a crack at polishing this up, getting the border right, vibrancy, etc.

Heyyyy I did it! This is much more representative of the system vibrancy, and works on all colors of backgrounds including solid black and solid white.

This does NOT include any animations. I stripped them out for now to get all the colors right. I'm pooped though, so I'll add the animations back tomorrow :slight_smile:

Great work getting all the cells working this way @yuuiko!

23%20PM 34%20PM 00%20PM

<!--
VISUAL WINDOW SNAPPING by the BetterTouchTool community.
Forum link: https://community.folivora.ai/t/visual-window-snapping/2863

Authors of this script:
- panda
- Jerosh
- GoldenChaos
- yyuuiko

Thanks for using it!
-->

<html>
<head>
    <style>
       .grid-container { /* The MAIN BACKGROUND of the pallete */
            display: grid;
            height: 150px;
            width: 150px;
            margin: 40px;
			padding: 1px;
			background: rgba(179, 178, 177, .5);
			-webkit-backdrop-filter: blur(48px) saturate(200%);
            border-radius: 12.5%;
            grid-template-columns: 1fr 1fr;
            grid-template-rows: 1fr 1fr;
            grid-gap: 0px 0px;
            grid-template-areas: "TopLeft TopRight""BottomLeft BottomRight";
            box-shadow: rgba(0,0,0,.5) 0 0 0 1px, rgba(0,0,0,.5) 0 12px 50px;
            overflow: hidden;
        }

        .highlighted { /* -------------------------------FADE (Representation)------------------------------------ */
			opacity: 0.7;
            -webkit-backdrop-filter: saturate(100%);
        }

        .highlightCell {
			opacity: 0.6;
            -webkit-backdrop-filter: saturate(100%);
        }

        .cell {
            background: rgba(49, 48, 44, .5);
            -webkit-backdrop-filter: saturate(100%);
            mix-blend-mode: multiply;
        }

        .TopLeft {
            display: grid;
            grid-area: TopLeft;
            grid-template-columns: 1fr 1fr;
            grid-template-rows: 1fr 1fr;
            grid-gap: 1px 1px;
            grid-template-areas: "TopLeftTopLeft TopLeftTopRight""TopLeftBottomLeft TopLeftBottomRight";
            border-top-left-radius: 25%;
          }

        .TopLeftTopLeft {
            grid-area: TopLeftTopLeft;
            border-top-left-radius: 50%;
        }

        .TopLeftBottomLeft {
            grid-area: TopLeftBottomLeft;
        }

        .TopLeftTopRight {
            grid-area: TopLeftTopRight;
        }

        .TopLeftBottomRight {
            grid-area: TopLeftBottomRight;
        }

        .TopRight {
            display: grid;
            grid-area: TopRight;
            grid-template-columns: 1fr 1fr;
            grid-template-rows: 1fr 1fr;
            grid-gap: 1px 1px;;
            grid-template-areas: "TopRightTopLeft TopRightTopRight""TopRightBottomLeft TopRightBottomRight";
            border-top-right-radius: 25%;
        }

        .TopRightTopLeft {
            grid-area: TopRightTopLeft;
        }

        .TopRightTopRight {
            grid-area: TopRightTopRight;
            border-top-right-radius: 50%;
        }

        .TopRightBottomLeft {
            grid-area: TopRightBottomLeft;
 */
        }

        .TopRightBottomRight {
            grid-area: TopRightBottomRight;
        }

        .BottomLeft {
            display: grid;
            grid-area: BottomLeft;
            grid-template-columns: 1fr 1fr;
            grid-template-rows: 1fr 1fr;
            grid-gap: 1px 1px;;
            grid-template-areas: "BottomLeftTopLeft BottomLeftTopRight""BottomLeftBottomLeft BottomLeftBottomRight";
            border-bottom-left-radius: 25%;
        }

        .BottomLeftTopLeft {
            grid-area: BottomLeftTopLeft;
        }

        .BottomLeftTopRight {
            grid-area: BottomLeftTopRight;

        }

        .BottomLeftBottomLeft {
            grid-area: BottomLeftBottomLeft;
            border-bottom-left-radius: 50%;
        }

        .BottomLeftBottomRight {
            grid-area: BottomLeftBottomRight;
        }

        .BottomRight {
            display: grid;
            grid-area: BottomRight;
            grid-template-columns: 1fr 1fr;
            grid-template-rows: 1fr 1fr;
            border-bottom-right-radius: 25%;
            grid-gap: 1px 1px;
            grid-template-areas: "BottomRightTopLeft BottomRightTopRight""BottomRightBottomLeft BottomRightBottomRight";
        }

        .BottomRightTopLeft {
            grid-area: BottomRightTopLeft;

        }

        .BottomRightTopRight {
            grid-area: BottomRightTopRight;
        }

        .BottomRightBottomLeft {
            grid-area: BottomRightBottomLeft;
        }

        .BottomRightBottomRight {
            grid-area: BottomRightBottomRight;
            border-bottom-right-radius: 50%;
        }
    </style>

    <script>
        window.currentlyHighlighted = [];

        //████████████████████████████- Add Highlight FX for Representation-██████████████
        function addHighlight(classNames) {
            for (var i = 0; i < classNames.length; i++) {
                window.currentlyHighlighted.push(classNames[i]);
                document.getElementsByClassName(classNames[i]).item(0).classList.add('highlighted');
            }
        }

        //████████████████████████████- Remove Highlight FX for Representation -██████████████
        function removeHighlight(classNames) {
            for (var i = 0; i < classNames.length; i++) {
                window.currentlyHighlighted = window.currentlyHighlighted.filter(e => e !== classNames[i]);
                document.getElementsByClassName(classNames[i]).item(0).classList.remove('highlighted');
                //document.getElementsByClassName(classNames[i]).item(0).classList.add('highlightOff');
            }
        }


        //████████████████████████████- Add Highlight FX for Button -██████████████
        function addCellHighlight(classNames) {
            for (var i = 0; i < classNames.length; i++) {
                window.currentlyHighlighted.push(classNames[i]);
                document.getElementsByClassName(classNames[i]).item(0).classList.add('highlightCell');
            }
        }

        //████████████████████████████- Remove Highlight FX for Button-██████████████
        function removeCellHighlight(classNames) {
            for (var i = 0; i < classNames.length; i++) {
                window.currentlyHighlighted = window.currentlyHighlighted.filter(e => e !== classNames[i]);
                document.getElementsByClassName(classNames[i]).item(0).classList.remove('highlightCell');
                //document.getElementsByClassName(classNames[i]).item(0).classList.add('highlightOff');
            }
        }

        function uniq(a) {
            var prims = {
                    "boolean": {},
                    "number": {},
                    "string": {}
                },
                objs = [];

            return a.filter(function(item) {
                var type = typeof item;
                if (type in prims)
                    return prims[type].hasOwnProperty(item) ? false : (prims[type][item] = true);
                else
                    return objs.indexOf(item) >= 0 ? false : objs.push(item);
            });
        }

        function performAction() {
            console.log(uniq(window.currentlyHighlighted));
            var trigger = '';
            if (window.currentlyHighlighted.indexOf('TopLeft') !== -1 &&
                window.currentlyHighlighted.indexOf('TopRight') !== -1 &&
                window.currentlyHighlighted.indexOf('BottomLeft') !== -1 &&
                window.currentlyHighlighted.indexOf('BottomRight') !== -1) {
                trigger = 'maximize';
            } else if (window.currentlyHighlighted.indexOf('TopLeft') !== -1 &&
                window.currentlyHighlighted.indexOf('TopRight') !== -1) {
                trigger = 'tophalf';
            } else if (window.currentlyHighlighted.indexOf('BottomLeft') !== -1 &&
                window.currentlyHighlighted.indexOf('BottomRight') !== -1) {
                trigger = 'bottomhalf';
            } else if (window.currentlyHighlighted.indexOf('TopLeft') !== -1 &&
                window.currentlyHighlighted.indexOf('BottomLeft') !== -1) {
                trigger = 'lefthalf';
            } else if (window.currentlyHighlighted.indexOf('TopRight') !== -1 &&
                window.currentlyHighlighted.indexOf('BottomRight') !== -1) {
                trigger = 'righthalf';
            } else if (window.currentlyHighlighted.indexOf('TopRight') !== -1) {
                trigger = 'topright';

            } else if (window.currentlyHighlighted.indexOf('TopLeft') !== -1) {
                trigger = 'topleft';

            } else if (window.currentlyHighlighted.indexOf('BottomLeft') !== -1) {
                trigger = 'bottomleft';

            } else if (window.currentlyHighlighted.indexOf('BottomRight') !== -1) {
                trigger = 'bottomright';

            }

            console.log('trigger', trigger);
            window.BTT.callHandler('trigger_named', {
                trigger_name: trigger,
                closeFloatingHTMLMenu: 1
            })
        }
    </script>
</head>
<body>
    <div class="grid-container" onclick="performAction()">
        <div class="TopLeft"                         onmouseover="addHighlight(['TopLeft']);" onmouseout="removeHighlight(['TopLeft']);">
            <div class="area-overlap TopLeftTopLeft cell" onmouseover="addHighlight(['TopLeftTopLeft']); addCellHighlight(['TopLeftTopLeft'])" onmouseout="removeHighlight(['TopLeftTopLeft']); removeCellHighlight(['TopLeftTopLeft'])"></div>
            <div class="TopLeftBottomLeft cell"           onmouseover="addHighlight(['BottomLeft', 'TopLeft']); addCellHighlight(['TopLeftBottomLeft', 'BottomLeftTopLeft'])" onmouseout="removeHighlight(['BottomLeft', 'TopLeft']); removeCellHighlight([ 'TopLeftBottomLeft', 'BottomLeftTopLeft'])"></div>
            <div class="TopLeftTopRight cell"             onmouseover="addHighlight(['TopRight', 'TopLeft']); addCellHighlight(['TopLeftTopRight', 'TopRightTopLeft']);" onmouseout="removeHighlight(['TopRight', 'TopLeft']); removeCellHighlight([ 'TopLeftTopRight', 'TopRightTopLeft'])"></div>
            <div class="TopLeftBottomRight cell"          onmouseover="addHighlight(['TopRight', 'BottomRight', 'BottomLeft', 'TopLeft']); addCellHighlight(['TopLeftBottomRight', 'TopRightBottomLeft', 'BottomLeftTopRight', 'BottomRightTopLeft'])" onmouseout="removeHighlight(['TopRight', 'BottomRight', 'BottomLeft', 'TopLeft']); removeCellHighlight(['TopLeftBottomRight', 'TopRightBottomLeft', 'BottomLeftTopRight', 'BottomRightTopLeft'])"></div>
        </div>

        <div class="TopRight"                        onmouseover="addHighlight(['TopRight'])" onmouseout="removeHighlight(['TopRight'])">
            <div class="area-overlap TopRightTopLeft cell"onmouseover="addHighlight(['TopRight', 'TopLeft']); addCellHighlight([ 'TopLeftTopRight', 'TopRightTopLeft'])" onmouseout="removeHighlight(['TopRight', 'TopLeft']); removeCellHighlight(['TopLeftTopRight', 'TopRightTopLeft'])"></div>
            <div class="TopRightTopRight cell"            onmouseover="addHighlight(['TopRightTopRight']); addCellHighlight(['TopRightTopRight'])" onmouseout="removeHighlight(['TopRightTopRight']); removeCellHighlight(['TopRightTopRight'])"></div>
            <div class="TopRightBottomLeft cell"          onmouseover="addHighlight(['TopRight', 'BottomRight', 'BottomLeft', 'TopLeft']); addCellHighlight(['TopLeftBottomRight', 'TopRightBottomLeft', 'BottomLeftTopRight', 'BottomRightTopLeft'])" onmouseout="removeHighlight(['TopRight', 'BottomRight', 'BottomLeft', 'TopLeft']); removeCellHighlight(['TopLeftBottomRight', 'TopRightBottomLeft', 'BottomLeftTopRight', 'BottomRightTopLeft'])"></div>
            <div class="TopRightBottomRight cell"         onmouseover="addHighlight(['TopRight', 'BottomRight']); addCellHighlight(['TopRightBottomRight', 'BottomRightTopRight'])" onmouseout="removeHighlight(['TopRight', 'BottomRight']); removeCellHighlight(['TopRightBottomRight', 'BottomRightTopRight'])"></div>
        </div>

        <div class="BottomLeft"                      onmouseover="addHighlight(['BottomLeft'])" onmouseout="removeHighlight(['BottomLeft'])">
            <div class="area-overlap BottomLeftTopLeft cell" onmouseover="addHighlight(['BottomLeft', 'TopLeft']); addCellHighlight([ 'TopLeftBottomLeft', 'BottomLeftTopLeft'])" onmouseout="removeHighlight(['BottomLeft', 'TopLeft']); removeCellHighlight(['TopLeftBottomLeft', 'BottomLeftTopLeft'])"></div>
            <div class="BottomLeftTopRight cell"          onmouseover="addHighlight(['TopRight', 'BottomRight', 'BottomLeft', 'TopLeft']); addCellHighlight([ 'TopLeftBottomRight', 'TopRightBottomLeft', 'BottomLeftTopRight', 'BottomRightTopLeft'])" onmouseout="removeHighlight(['TopRight', 'BottomRight', 'BottomLeft', 'TopLeft']); removeCellHighlight(['TopLeftBottomRight', 'TopRightBottomLeft', 'BottomLeftTopRight', 'BottomRightTopLeft'])"></div>
            <div class="BottomLeftBottomLeft cell"        onmouseover="addHighlight(['BottomLeftBottomLeft']); addCellHighlight(['BottomLeftBottomLeft'])" onmouseout="removeHighlight(['BottomLeftBottomLeft']); removeCellHighlight(['BottomLeftBottomLeft'])"></div>
            <div class="BottomLeftBottomRight cell"       onmouseover="addHighlight(['BottomLeft', 'BottomRight']); addCellHighlight(['BottomRightBottomLeft', 'BottomLeftBottomRight'])" onmouseout="removeHighlight(['BottomLeft', 'BottomRight']); removeCellHighlight([ 'BottomRightBottomLeft', 'BottomLeftBottomRight'])"></div>
        </div>

        <div class="BottomRight"                      onmouseover="addHighlight(['BottomRight'])" onmouseout="removeHighlight(['BottomRight'])">
            <div class="area-overlap BottomRightTopLeft cell" onmouseover="addHighlight(['TopRight', 'BottomRight', 'BottomLeft', 'TopLeft']); addCellHighlight([ 'TopLeftBottomRight', 'TopRightBottomLeft', 'BottomLeftTopRight', 'BottomRightTopLeft'])" onmouseout="removeHighlight(['TopRight', 'BottomRight', 'BottomLeft', 'TopLeft']); removeCellHighlight(['TopLeftBottomRight', 'TopRightBottomLeft', 'BottomLeftTopRight', 'BottomRightTopLeft'])"></div>
            <div class="BottomRightTopRight cell"          onmouseover="addHighlight(['BottomRight', 'TopRight']); addCellHighlight(['TopRightBottomRight', 'BottomRightTopRight'])" onmouseout="removeHighlight(['BottomRight', 'TopRight']); removeCellHighlight(['TopRightBottomRight', 'BottomRightTopRight'])"></div>
            <div class="BottomRightBottomLeft cell"        onmouseover="addHighlight(['BottomLeft', 'BottomRight']); addCellHighlight(['BottomRightBottomLeft', 'BottomLeftBottomRight'])" onmouseout="removeHighlight(['BottomLeft', 'BottomRight']); removeCellHighlight([ 'BottomRightBottomLeft', 'BottomLeftBottomRight'])"></div>
            <div class="BottomRightBottomRight cell"       onmouseover="addHighlight(['BottomRightBottomRight']); addCellHighlight(['BottomRightBottomRight'])" onmouseout="removeHighlight(['BottomRightBottomRight']); removeCellHighlight(['BottomRightBottomRight'])"></div>
        </div>
    </div>
</body>
</html>
2 Likes

@GoldenChaos, @panda, @peripatew, @jerosh,

Visual Window Snapping + Monitors

UI Now Working!!

I've just done the chore of duplicating and differentiating each of the three palettes, making styles for each, copying the seperate styles into each palette and re-writing the Javascript two times over for each palette. There's basically three window snapping presets in here now, but the UI (which can be seen here, i wasn't bothered to film it again) is now working!

The functionality on BTT's side still isn't configured, but the HTML and JS should correctly call the right trigger, as i'm seeing the correct names in safari's console.

I've added an 'L' or an 'R' to the class names / BTT triggers to be created to indicate monitor snapping.

RLeftHalf: Snap to the left half of the monitor to the right
LeftHalf: Snap to the left half of this monitor
LLeftHalf: Snap to the left half of the monitor to the left

Most of the code is the same except with added 'L's and 'R's. (it still took ages to get all of them)


So, the messy UI has been fixed and it's returning the right trigger names, now:

  • Need to make the actual named triggers in BTT, right now only the original 'this window' ones are there.
  • Need to touchup the aesthetics a bit. The main functionality is there but the borders and such need to be refined. Stretch Goal: transform the side palletes into circles with icons when shrunk.
  • Need to make an applescript that senses if any external displays are active, then shows the correct snapping HTML. (either the one with the desktop snapping or just the normal small one.) This will be called by the user, it then displays the right one based on the current situation.

I'm not sure how to make those window snapping triggers so it would be great if someone made a bunch for both L/R monitor switching!


Here is the code so far if anyone wants to have a look:

Window Snapping + Monitor Snapping
<!--
VISUAL WINDOW SNAPPING by the BetterTouchTool community.
Forum link: https://community.folivora.ai/t/visual-window-snapping/2863

Authors of this script:
- panda: The Core Original Workings
- Jerosh: Highlighting Improvements
- GoldenChaos: Colouring and Aesthetic Improvements
- yyuuiko: Multi-Desktop Snapping and Code Optimisation

Thanks for using it!
-->

<html>
<head>
    <style>



      html { /* background image for aesthetic testing */
        /* background-image: url('red.png');
        background-color: #def;
        background-repeat: no-repeat;
        background-size: cover; */

      /* Image Centering */
        background-position: 50% 50%;   /* Background Position */
        width: 100vw;                   /* Body Width (fill viewport width) */
        height: 100vh;                  /* Body Height (fill viewport height) */
      }

      .main-container { /* MAIN WRAPPER */
        margin: 40px;
        left: calc(50% - 17.3em);
        position: fixed;
        overflow: visible;
        /* margin-left:; */
      }

      .grid-container { /* MAIN GRID */
        /* display: grid;
        height: 150px;
        width: 150px;
        padding: 0px;
        background-color: rgba(255,255,255,0.5);
        -webkit-backdrop-filter: blur(20px);
        border-radius: 38px;
        -webkit-background-clip: padding-box;
        background-clip: padding-box;
        grid-template-columns: 1fr 1fr;
        grid-template-rows: 1fr 1fr;
        grid-gap: 0px 0px;
        grid-template-areas: "TopLeft TopRight""BottomLeft BottomRight";
        box-shadow: rgba(0,0,0,.5) 0 0 0 1px, rgba(0,0,0,.5) 0 12px 50px;
        overflow: hidden;
        border: 1.5px solid rgba(255,255,255,0.7); */

        display: grid;
        height: 150px;
        width: 150px;
        margin-left: 40px;
        margin-right: 40px;
        padding: 1px;
        background: rgba(179, 178, 177, .5);
        -webkit-backdrop-filter: blur(48px) saturate(200%);
        border-radius: 38px;
        grid-template-columns: 1fr 1fr;
        grid-template-rows: 1fr 1fr;
        grid-gap: 0px 0px;
        grid-template-areas: "TopLeft TopRight""BottomLeft BottomRight";
        box-shadow: rgba(0,0,0,.5) 0 0 0 1px, rgba(0,0,0,.5) 0 12px 50px;
        overflow: hidden;


        -webkit-transition: all 600ms cubic-bezier(0.19, 1, 0.22, 1); /* easeOutExpo */
        transition: all 600ms cubic-bezier(0.19, 1, 0.22, 1); /* easeOutExpo */
      }

/* ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ Monitor Switchers ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ */
        .monitor {
          /* display: grid;
          height: 150px;
          width: 150px;
          transform: scale(.4,.4);
          padding: 0px;
          background-color: rgba(255,255,255,0.5);
          -webkit-backdrop-filter: blur(20px);
          border-radius: 38px;
          -webkit-background-clip: padding-box;
          background-clip: padding-box;
          grid-template-columns: 1fr 1fr;
          grid-template-rows: 1fr 1fr;
          grid-gap: 0px 0px;
          grid-template-areas: "TopLeft TopRight""BottomLeft BottomRight";
          box-shadow: rgba(0,0,0,.5) 0 0 0 1px, rgba(0,0,0,.5) 0 12px 50px;
          overflow: hidden;
          border: 1.5px solid rgba(255,255,255,0.7); */

          transform: scale(.4,.4);

          display: grid;
          height: 150px;
          width: 150px;
          margin-left: 40px;
          margin-right: 40px;
          padding: 1px;
          background: rgba(179, 178, 177, .5);
          -webkit-backdrop-filter: blur(48px) saturate(200%);
          border-radius: 38px;
          grid-template-columns: 1fr 1fr;
          grid-template-rows: 1fr 1fr;
          grid-gap: 0px 0px;
          grid-template-areas: "TopLeft TopRight""BottomLeft BottomRight";
          box-shadow: rgba(0,0,0,.5) 0 0 0 1px, rgba(0,0,0,.5) 0 12px 50px;
          overflow: hidden;

          -webkit-transition: all 600ms cubic-bezier(0.19, 1, 0.22, 1); /* easeOutExpo */
          transition: all 600ms cubic-bezier(0.19, 1, 0.22, 1); /* easeOutExpo */
        }

        .monitor-left {
          float: left;
          margin-right: -30px;
        }

        .monitor-right{
          float: right;
          margin-left: -30px;
        }

/* ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ EXPAND MONITOR PALLETES ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ */

        .expandPallete {
          transform: scale(1,1);
          border-radius: 30%;

          -webkit-transition: all 600ms cubic-bezier(0.19, 1, 0.22, 1); /* easeOutExpo */
          transition: all 600ms cubic-bezier(0.19, 1, 0.22, 1); /* easeOutExpo */
        }

        .shrinkPallete {
          transform: scale(.4,.4);

          -webkit-transition: all 600ms cubic-bezier(0.19, 1, 0.22, 1); /* easeOutExpo */
          transition: all 600ms cubic-bezier(0.19, 1, 0.22, 1); /* easeOutExpo */
        }

/* ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ CELL HIGHLIGHTS ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ */
        .highlighted { /* REPRESENTATION Style */
          -webkit-backdrop-filter: brightness(100%) saturate(180%);
          background-color: rgba(255,255,255,.45);

          /* opacity: 0.7;
          -webkit-backdrop-filter: saturate(100%) */

          -webkit-transition: all 600ms cubic-bezier(0.19, 1, 0.22, 1); /* easeOutExpo */
          transition: all 600ms cubic-bezier(0.19, 1, 0.22, 1); /* easeOutExpo */
        }

        .highlightCell { /* BUTTON Style */
          /* background-color: rgba(255,0,0,1); */
          /* -webkit-backdrop-filter: brightness(100%) saturate(100%); */

          opacity: 0.7;
          -webkit-backdrop-filter: saturate(100%);

          -webkit-transition: -webkit-backdrop-filter 600ms cubic-bezier(0.19, 1, 0.22, 1);
          transition: -webkit-backdrop-filter 600ms cubic-bezier(0.19, 1, 0.72, 1);
        }

/* ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ FADE (Representation) ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ */

        .cell {
/**/        /* border: solid rgba(255,255,255,0); */
            /* background-color: rgba(0,0,0,.4); */
            /* -webkit-backdrop-filter: saturate(180%) brightness(50%);
            -webkit-background-clip: padding-box;
            background-clip: padding-box;
            mix-blend-mode: multiply; */

            background: rgba(49, 48, 44, .5);
            -webkit-backdrop-filter: saturate(100%);
            mix-blend-mode: multiply;
        }

/*------------------------------------------------------------------------------------------------------------------------*/
        .TopLeft {
            display: grid;
            grid-area: TopLeft;
            grid-template-columns: 1fr 1fr;
            grid-template-rows: 1fr 1fr;
/**/            grid-gap: 1px 1px;
            grid-template-areas: "TopLeftTopLeft TopLeftTopRight""TopLeftBottomLeft TopLeftBottomRight";
            border-top-left-radius: 36px;
          }

        .TopLeftTopLeft {
            grid-area: TopLeftTopLeft;
            border-top-left-radius: 36px;
/*            border-width: 0px 1px 1px 0px; */
            -webkit-background-clip: padding-box;
            background-clip: padding-box;
        }

        .TopLeftBottomLeft {
            grid-area: TopLeftBottomLeft;
/*            border-width: 1px 1px 0px 0px; */
        }

        .TopLeftTopRight {
            grid-area: TopLeftTopRight;
/*            border-width: 0px 0px 1px 1px; */
        }

        .TopLeftBottomRight {
            grid-area: TopLeftBottomRight;
/*            border-width: 1px 0px 0px 1px; */
        }


/*------------------------------------------------------------------------------------------------------------------------*/
        .LTopLeft {
            display: grid;
            grid-area: TopLeft;
            grid-template-columns: 1fr 1fr;
            grid-template-rows: 1fr 1fr;
/**/            grid-gap: 1px 1px;
            grid-template-areas: "TopLeftTopLeft TopLeftTopRight""TopLeftBottomLeft TopLeftBottomRight";
            border-top-left-radius: 36px;
          }

        .LTopLeftTopLeft {
            grid-area: TopLeftTopLeft;
            border-top-left-radius: 36px;
/*            border-width: 0px 1px 1px 0px; */
            -webkit-background-clip: padding-box;
            background-clip: padding-box;
        }

        .LTopLeftBottomLeft {
            grid-area: TopLeftBottomLeft;
/*            border-width: 1px 1px 0px 0px; */
        }

        .LTopLeftTopRight {
            grid-area: TopLeftTopRight;
/*            border-width: 0px 0px 1px 1px; */
        }

        .LTopLeftBottomRight {
            grid-area: TopLeftBottomRight;
/*            border-width: 1px 0px 0px 1px; */
        }

/*------------------------------------------------------------------------------------------------------------------------*/
        .RTopLeft {
            display: grid;
            grid-area: TopLeft;
            grid-template-columns: 1fr 1fr;
            grid-template-rows: 1fr 1fr;
/**/            grid-gap: 1px 1px;
            grid-template-areas: "TopLeftTopLeft TopLeftTopRight""TopLeftBottomLeft TopLeftBottomRight";
            border-top-left-radius: 36px;
          }

        .RTopLeftTopLeft {
            grid-area: TopLeftTopLeft;
            border-top-left-radius: 36px;
/*            border-width: 0px 1px 1px 0px; */
            -webkit-background-clip: padding-box;
            background-clip: padding-box;
        }

        .RTopLeftBottomLeft {
            grid-area: TopLeftBottomLeft;
/*            border-width: 1px 1px 0px 0px; */
        }

        .RTopLeftTopRight {
            grid-area: TopLeftTopRight;
/*            border-width: 0px 0px 1px 1px; */
        }

        .RTopLeftBottomRight {
            grid-area: TopLeftBottomRight;
/*            border-width: 1px 0px 0px 1px; */
        }


/*------------------------------------------------------------------------------------------------------------------------*/
        .TopRight {
            display: grid;
            grid-area: TopRight;
            grid-template-columns: 1fr 1fr;
            grid-template-rows: 1fr 1fr;
/**/            grid-gap: 1px 1px;;
            grid-template-areas: "TopRightTopLeft TopRightTopRight""TopRightBottomLeft TopRightBottomRight";
            border-top-right-radius: 36px;
        }

        .TopRightTopLeft {
            grid-area: TopRightTopLeft;
/*            border-width: 0px 1px 1px 0px; */
        }

        .TopRightTopRight {
            grid-area: TopRightTopRight;
            border-top-right-radius: 36px;
/*            border-width: 0px 0px 1px 1px; */
            -webkit-background-clip: padding-box;
            background-clip: padding-box;
        }

        .TopRightBottomLeft {
            grid-area: TopRightBottomLeft;
/*            border-width: 1px 1px 0px 0px; */
        }

        .TopRightBottomRight {
            grid-area: TopRightBottomRight;
/*            border-width: 1px 0px 0px 1px; */
        }


/*------------------------------------------------------------------------------------------------------------------------*/
        .LTopRight {
            display: grid;
            grid-area: TopRight;
            grid-template-columns: 1fr 1fr;
            grid-template-rows: 1fr 1fr;
/**/            grid-gap: 1px 1px;;
            grid-template-areas: "TopRightTopLeft TopRightTopRight""TopRightBottomLeft TopRightBottomRight";
            border-top-right-radius: 36px;
        }

        .LTopRightTopLeft {
            grid-area: TopRightTopLeft;
/*            border-width: 0px 1px 1px 0px; */
        }

        .LTopRightTopRight {
            grid-area: TopRightTopRight;
            border-top-right-radius: 36px;
/*            border-width: 0px 0px 1px 1px; */
            -webkit-background-clip: padding-box;
            background-clip: padding-box;
        }

        .LTopRightBottomLeft {
            grid-area: TopRightBottomLeft;
/*            border-width: 1px 1px 0px 0px; */
        }

        .LTopRightBottomRight {
            grid-area: TopRightBottomRight;
/*            border-width: 1px 0px 0px 1px; */
        }


/*------------------------------------------------------------------------------------------------------------------------*/
        .RTopRight {
            display: grid;
            grid-area: TopRight;
            grid-template-columns: 1fr 1fr;
            grid-template-rows: 1fr 1fr;
/**/            grid-gap: 1px 1px;;
            grid-template-areas: "TopRightTopLeft TopRightTopRight""TopRightBottomLeft TopRightBottomRight";
            border-top-right-radius: 36px;
        }

        .RTopRightTopLeft {
            grid-area: TopRightTopLeft;
/*            border-width: 0px 1px 1px 0px; */
        }

        .RTopRightTopRight {
            grid-area: TopRightTopRight;
            border-top-right-radius: 36px;
/*            border-width: 0px 0px 1px 1px; */
            -webkit-background-clip: padding-box;
            background-clip: padding-box;
        }

        .RTopRightBottomLeft {
            grid-area: TopRightBottomLeft;
/*            border-width: 1px 1px 0px 0px; */
        }

        .RTopRightBottomRight {
            grid-area: TopRightBottomRight;
/*            border-width: 1px 0px 0px 1px; */
        }


        /*------------------------------------------------------------------------------------------------------------------------*/
        .BottomLeft {
            display: grid;
            grid-area: BottomLeft;
            grid-template-columns: 1fr 1fr;
            grid-template-rows: 1fr 1fr;
/**/            grid-gap: 1px 1px;;
            grid-template-areas: "BottomLeftTopLeft BottomLeftTopRight""BottomLeftBottomLeft BottomLeftBottomRight";
            border-bottom-left-radius: 36px;
        }

        .BottomLeftTopLeft {
            grid-area: BottomLeftTopLeft;
/*            border-width: 0px 1px 1px 0px; */

        }

        .BottomLeftTopRight {
            grid-area: BottomLeftTopRight;
/*            border-width: 0px 0px 1px 1px; */

        }

        .BottomLeftBottomLeft {
            grid-area: BottomLeftBottomLeft;
            border-bottom-left-radius: 36px;
/*            border-width: 1px 1px 0px 0px; */
            -webkit-background-clip: padding-box;
            background-clip: padding-box;
        }

        .BottomLeftBottomRight {
            grid-area: BottomLeftBottomRight;
/*            border-width: 1px 0px 0px 1px; */
        }


        /*------------------------------------------------------------------------------------------------------------------------*/
        .LBottomLeft {
          display: grid;
          grid-area: BottomLeft;
          grid-template-columns: 1fr 1fr;
          grid-template-rows: 1fr 1fr;
          /**/            grid-gap: 1px 1px;;
          grid-template-areas: "BottomLeftTopLeft BottomLeftTopRight""BottomLeftBottomLeft BottomLeftBottomRight";
          border-bottom-left-radius: 36px;
        }

        .LBottomLeftTopLeft {
          grid-area: BottomLeftTopLeft;
          /*            border-width: 0px 1px 1px 0px; */

        }

        .LBottomLeftTopRight {
          grid-area: BottomLeftTopRight;
          /*            border-width: 0px 0px 1px 1px; */

        }

        .LBottomLeftBottomLeft {
          grid-area: BottomLeftBottomLeft;
          border-bottom-left-radius: 36px;
          /*            border-width: 1px 1px 0px 0px; */
          -webkit-background-clip: padding-box;
          background-clip: padding-box;
        }

        .LBottomLeftBottomRight {
          grid-area: BottomLeftBottomRight;
          /*            border-width: 1px 0px 0px 1px; */
        }


/*------------------------------------------------------------------------------------------------------------------------*/
        .RBottomLeft {
            display: grid;
            grid-area: BottomLeft;
            grid-template-columns: 1fr 1fr;
            grid-template-rows: 1fr 1fr;
/**/            grid-gap: 1px 1px;;
            grid-template-areas: "BottomLeftTopLeft BottomLeftTopRight""BottomLeftBottomLeft BottomLeftBottomRight";
            border-bottom-left-radius: 36px;
        }

        .RBottomLeftTopLeft {
            grid-area: BottomLeftTopLeft;
/*            border-width: 0px 1px 1px 0px; */

        }

        .RBottomLeftTopRight {
            grid-area: BottomLeftTopRight;
/*            border-width: 0px 0px 1px 1px; */

        }

        .RBottomLeftBottomLeft {
            grid-area: BottomLeftBottomLeft;
            border-bottom-left-radius: 36px;
/*            border-width: 1px 1px 0px 0px; */
            -webkit-background-clip: padding-box;
            background-clip: padding-box;
        }

        .RBottomLeftBottomRight {
            grid-area: BottomLeftBottomRight;
/*            border-width: 1px 0px 0px 1px; */
        }


        /*------------------------------------------------------------------------------------------------------------------------*/
        .BottomRight {
            display: grid;
            grid-area: BottomRight;
            grid-template-columns: 1fr 1fr;
            grid-template-rows: 1fr 1fr;
            border-bottom-right-radius: 36px;
/**/            grid-gap: 1px 1px;;
            grid-template-areas: "BottomRightTopLeft BottomRightTopRight""BottomRightBottomLeft BottomRightBottomRight";
        }

        .BottomRightTopLeft {
            grid-area: BottomRightTopLeft;
/*            border-width: 0px 1px 1px 0px; */

        }

        .BottomRightTopRight {
            grid-area: BottomRightTopRight;
/*            border-width: 0px 0px 1px 1px; */
            -webkit-background-clip: padding-box;
            background-clip: padding-box;
        }

        .BottomRightBottomLeft {
            grid-area: BottomRightBottomLeft;
/*            border-width: 1px 1px 0px 0px; */
        }

        .BottomRightBottomRight {
            grid-area: BottomRightBottomRight;
            border-bottom-right-radius: 36px;
/*            border-width: 1px 0px 0px 1px; */
        }

        /*------------------------------------------------------------------------------------------------------------------------*/
        .LBottomRight {
            display: grid;
            grid-area: BottomRight;
            grid-template-columns: 1fr 1fr;
            grid-template-rows: 1fr 1fr;
            border-bottom-right-radius: 36px;
/**/            grid-gap: 1px 1px;;
            grid-template-areas: "BottomRightTopLeft BottomRightTopRight""BottomRightBottomLeft BottomRightBottomRight";
        }

        .LBottomRightTopLeft {
            grid-area: BottomRightTopLeft;
/*            border-width: 0px 1px 1px 0px; */

        }

        .LBottomRightTopRight {
            grid-area: BottomRightTopRight;
/*            border-width: 0px 0px 1px 1px; */
            -webkit-background-clip: padding-box;
            background-clip: padding-box;
        }

        .LBottomRightBottomLeft {
            grid-area: BottomRightBottomLeft;
/*            border-width: 1px 1px 0px 0px; */
        }

        .LBottomRightBottomRight {
            grid-area: BottomRightBottomRight;
            border-bottom-right-radius: 36px;
/*            border-width: 1px 0px 0px 1px; */
        }

        /*------------------------------------------------------------------------------------------------------------------------*/
        .RBottomRight {
            display: grid;
            grid-area: BottomRight;
            grid-template-columns: 1fr 1fr;
            grid-template-rows: 1fr 1fr;
            border-bottom-right-radius: 36px;
/**/            grid-gap: 1px 1px;;
            grid-template-areas: "BottomRightTopLeft BottomRightTopRight""BottomRightBottomLeft BottomRightBottomRight";
        }

        .RBottomRightTopLeft {
            grid-area: BottomRightTopLeft;
/*            border-width: 0px 1px 1px 0px; */

        }

        .RBottomRightTopRight {
            grid-area: BottomRightTopRight;
/*            border-width: 0px 0px 1px 1px; */
            -webkit-background-clip: padding-box;
            background-clip: padding-box;
        }

        .RBottomRightBottomLeft {
            grid-area: BottomRightBottomLeft;
/*            border-width: 1px 1px 0px 0px; */
        }

        .RBottomRightBottomRight {
            grid-area: BottomRightBottomRight;
            border-bottom-right-radius: 36px;
/*            border-width: 1px 0px 0px 1px; */
        }
    </style>

    <script>
        window.currentlyHighlighted = [];

        //████████████████████████████- Add Highlight FX for Representation-██████████████
        function addHighlight(classNames) {
            for (var i = 0; i < classNames.length; i++) {
                window.currentlyHighlighted.push(classNames[i]);
                document.getElementsByClassName(classNames[i]).item(0).classList.add('highlighted');
            }
        }

        //████████████████████████████- Remove Highlight FX for Representation -██████████████
        function removeHighlight(classNames) {
            for (var i = 0; i < classNames.length; i++) {
                window.currentlyHighlighted = window.currentlyHighlighted.filter(e => e !== classNames[i]);
                document.getElementsByClassName(classNames[i]).item(0).classList.remove('highlighted');
            }
        }

        //████████████████████████████- Add Highlight FX for Button -██████████████
        function addCellHighlight(classNames) {
            for (var i = 0; i < classNames.length; i++) {
                window.currentlyHighlighted.push(classNames[i]);
                document.getElementsByClassName(classNames[i]).item(0).classList.add('highlightCell');
            }
        }

        //████████████████████████████- Remove Highlight FX for Button-██████████████
        function removeCellHighlight(classNames) {
            for (var i = 0; i < classNames.length; i++) {
                window.currentlyHighlighted = window.currentlyHighlighted.filter(e => e !== classNames[i]);
                document.getElementsByClassName(classNames[i]).item(0).classList.remove('highlightCell');
            }
        }


        //████████████████████████████- Add Expand for Monitor Palletes -██████████████
        function addExpand(classNames) {
            for (var i = 0; i < classNames.length; i++) {
                window.currentlyHighlighted.push(classNames[i]);
                document.getElementsByClassName(classNames[i]).item(0).classList.add('expandPallete');
                document.getElementsByClassName('grid-container').item(0).classList.add('shrinkPallete');
            }
        }

        //████████████████████████████- Remove Expand for Monitor Palletes-██████████████
        function removeExpand(classNames) {
            for (var i = 0; i < classNames.length; i++) {
                window.currentlyHighlighted = window.currentlyHighlighted.filter(e => e !== classNames[i]);
                document.getElementsByClassName(classNames[i]).item(0).classList.remove('expandPallete');
                document.getElementsByClassName('grid-container').item(0).classList.remove('shrinkPallete');
            }
        }

        function uniq(a) {
            var prims = {
                    "boolean": {},
                    "number": {},
                    "string": {}
                },
                objs = [];

            return a.filter(function(item) {
                var type = typeof item;
                if (type in prims)
                    return prims[type].hasOwnProperty(item) ? false : (prims[type][item] = true);
                else
                    return objs.indexOf(item) >= 0 ? false : objs.push(item);
            });
        }

        function performAction() {
            console.log(uniq(window.currentlyHighlighted));
            var trigger = '';
            if (window.currentlyHighlighted.indexOf('TopLeft') !== -1 &&
                window.currentlyHighlighted.indexOf('TopRight') !== -1 &&
                window.currentlyHighlighted.indexOf('BottomLeft') !== -1 &&
                window.currentlyHighlighted.indexOf('BottomRight') !== -1) {
                trigger = 'maximize';
            } else if (window.currentlyHighlighted.indexOf('TopLeft') !== -1 &&
                window.currentlyHighlighted.indexOf('TopRight') !== -1) {
                trigger = 'tophalf';
            } else if (window.currentlyHighlighted.indexOf('BottomLeft') !== -1 &&
                window.currentlyHighlighted.indexOf('BottomRight') !== -1) {
                trigger = 'bottomhalf';
            } else if (window.currentlyHighlighted.indexOf('TopLeft') !== -1 &&
                window.currentlyHighlighted.indexOf('BottomLeft') !== -1) {
                trigger = 'lefthalf';
            } else if (window.currentlyHighlighted.indexOf('TopRight') !== -1 &&
                window.currentlyHighlighted.indexOf('BottomRight') !== -1) {
                trigger = 'righthalf';
            } else if (window.currentlyHighlighted.indexOf('TopRight') !== -1) {
                trigger = 'topright';

            } else if (window.currentlyHighlighted.indexOf('TopLeft') !== -1) {
                trigger = 'topleft';

            } else if (window.currentlyHighlighted.indexOf('BottomLeft') !== -1) {
                trigger = 'bottomleft';

            } else if (window.currentlyHighlighted.indexOf('BottomRight') !== -1) {
                trigger = 'bottomright';

            }

            console.log('trigger', trigger);
            window.BTT.callHandler('trigger_named', {
                trigger_name: trigger,
                closeFloatingHTMLMenu: 1
            })
        }

        function performLeftAction() {
            console.log(uniq(window.currentlyHighlighted));
            var trigger = '';
            if (window.currentlyHighlighted.indexOf('TopLeft') !== -1 &&
                window.currentlyHighlighted.indexOf('TopRight') !== -1 &&
                window.currentlyHighlighted.indexOf('BottomLeft') !== -1 &&
                window.currentlyHighlighted.indexOf('BottomRight') !== -1) {
                trigger = 'maximize';
            } else if (window.currentlyHighlighted.indexOf('TopLeft') !== -1 &&
                window.currentlyHighlighted.indexOf('TopRight') !== -1) {
                trigger = 'tophalf';
            } else if (window.currentlyHighlighted.indexOf('BottomLeft') !== -1 &&
                window.currentlyHighlighted.indexOf('BottomRight') !== -1) {
                trigger = 'bottomhalf';
            } else if (window.currentlyHighlighted.indexOf('TopLeft') !== -1 &&
                window.currentlyHighlighted.indexOf('BottomLeft') !== -1) {
                trigger = 'lefthalf';
            } else if (window.currentlyHighlighted.indexOf('TopRight') !== -1 &&
                window.currentlyHighlighted.indexOf('BottomRight') !== -1) {
                trigger = 'righthalf';
            } else if (window.currentlyHighlighted.indexOf('TopRight') !== -1) {
                trigger = 'topright';

            } else if (window.currentlyHighlighted.indexOf('TopLeft') !== -1) {
                trigger = 'topleft';

            } else if (window.currentlyHighlighted.indexOf('BottomLeft') !== -1) {
                trigger = 'bottomleft';

            } else if (window.currentlyHighlighted.indexOf('BottomRight') !== -1) {
                trigger = 'bottomright';

            }

            console.log('trigger', trigger);
            window.BTT.callHandler('trigger_named', {
                trigger_name: trigger,
                closeFloatingHTMLMenu: 1
            })
        }

        function performRightAction() {
            console.log(uniq(window.currentlyHighlighted));
            var trigger = '';
            if (window.currentlyHighlighted.indexOf('RTopLeft') !== -1 &&
                window.currentlyHighlighted.indexOf('RTopRight') !== -1 &&
                window.currentlyHighlighted.indexOf('RBottomLeft') !== -1 &&
                window.currentlyHighlighted.indexOf('RBottomRight') !== -1) {
                trigger = 'Rmaximize';
            } else if (window.currentlyHighlighted.indexOf('RTopLeft') !== -1 &&
                window.currentlyHighlighted.indexOf('RTopRight') !== -1) {
                trigger = 'Rtophalf';
            } else if (window.currentlyHighlighted.indexOf('RBottomLeft') !== -1 &&
                window.currentlyHighlighted.indexOf('RBottomRight') !== -1) {
                trigger = 'Rbottomhalf';
            } else if (window.currentlyHighlighted.indexOf('RTopLeft') !== -1 &&
                window.currentlyHighlighted.indexOf('RBottomLeft') !== -1) {
                trigger = 'Rlefthalf';
            } else if (window.currentlyHighlighted.indexOf('RTopRight') !== -1 &&
                window.currentlyHighlighted.indexOf('RBottomRight') !== -1) {
                trigger = 'Rrighthalf';
            } else if (window.currentlyHighlighted.indexOf('RTopRight') !== -1) {
                trigger = 'Rtopright';

            } else if (window.currentlyHighlighted.indexOf('RTopLeft') !== -1) {
                trigger = 'Rtopleft';

            } else if (window.currentlyHighlighted.indexOf('RBottomLeft') !== -1) {
                trigger = 'Rbottomleft';

            } else if (window.currentlyHighlighted.indexOf('RBottomRight') !== -1) {
                trigger = 'Rbottomright';

            }

            console.log('trigger', trigger);
            window.BTT.callHandler('trigger_named', {
                trigger_name: trigger,
                closeFloatingHTMLMenu: 1
            })
        }
    </script>
</head>
<body>
  <div class="main-container">

<!-- Left Monitor Pallete -->
    <div class="monitor monitor-left" onclick="performLeftAction()" onmouseover="addExpand(['monitor-left']);" onmouseout="removeExpand(['monitor-left']);">
      <div class="LTopLeft"                         onmouseover="addHighlight(['LTopLeft']);" onmouseout="removeHighlight(['LTopLeft']);">
        <div class="LTopLeftTopLeft area-overlap cell" onmouseover="addHighlight(['LTopLeftTopLeft']); addCellHighlight(['LTopLeftTopLeft'])" onmouseout="removeHighlight(['LTopLeftTopLeft']); removeCellHighlight(['LTopLeftTopLeft'])"></div>
        <div class="LTopLeftBottomLeft cell"           onmouseover="addHighlight(['LBottomLeft', 'LTopLeft']); addCellHighlight(['LTopLeftBottomLeft', 'LBottomLeftTopLeft'])" onmouseout="removeHighlight(['LBottomLeft', 'LTopLeft']); removeCellHighlight([ 'LTopLeftBottomLeft', 'LBottomLeftTopLeft'])"></div>
        <div class="LTopLeftTopRight cell"             onmouseover="addHighlight(['LTopRight', 'LTopLeft']); addCellHighlight(['LTopLeftTopRight', 'LTopRightTopLeft']);" onmouseout="removeHighlight(['LTopRight', 'LTopLeft']); removeCellHighlight([ 'LTopLeftTopRight', 'LTopRightTopLeft'])"></div>
        <div class="LTopLeftBottomRight cell"          onmouseover="addHighlight(['LTopRight', 'LBottomRight', 'LBottomLeft', 'LTopLeft']); addCellHighlight(['LTopLeftBottomRight', 'LTopRightBottomLeft', 'LBottomLeftTopRight', 'LBottomRightTopLeft'])" onmouseout="removeHighlight(['LTopRight', 'LBottomRight', 'LBottomLeft', 'LTopLeft']); removeCellHighlight(['LTopLeftBottomRight', 'LTopRightBottomLeft', 'LBottomLeftTopRight', 'LBottomRightTopLeft'])"></div>
      </div>

      <div class="LTopRight"                        onmouseover="addHighlight(['LTopRight'])" onmouseout="removeHighlight(['LTopRight'])">
        <div class="LTopRightTopLeft area-overlap cell"onmouseover="addHighlight(['LTopRight', 'LTopLeft']); addCellHighlight([ 'LTopLeftTopRight', 'LTopRightTopLeft'])" onmouseout="removeHighlight(['LTopRight', 'LTopLeft']); removeCellHighlight(['LTopLeftTopRight', 'LTopRightTopLeft'])"></div>
        <div class="LTopRightTopRight cell"            onmouseover="addHighlight(['LTopRightTopRight']); addCellHighlight(['LTopRightTopRight'])" onmouseout="removeHighlight(['LTopRightTopRight']); removeCellHighlight(['LTopRightTopRight'])"></div>
        <div class="LTopRightBottomLeft cell"          onmouseover="addHighlight(['LTopRight', 'LBottomRight', 'LBottomLeft', 'LTopLeft']); addCellHighlight(['LTopLeftBottomRight', 'LTopRightBottomLeft', 'LBottomLeftTopRight', 'LBottomRightTopLeft'])" onmouseout="removeHighlight(['LTopRight', 'LBottomRight', 'LBottomLeft', 'LTopLeft']); removeCellHighlight(['LTopLeftBottomRight', 'LTopRightBottomLeft', 'LBottomLeftTopRight', 'LBottomRightTopLeft'])"></div>
        <div class="LTopRightBottomRight cell"         onmouseover="addHighlight(['LTopRight', 'LBottomRight']); addCellHighlight(['LTopRightBottomRight', 'LBottomRightTopRight'])" onmouseout="removeHighlight(['LTopRight', 'LBottomRight']); removeCellHighlight(['LTopRightBottomRight', 'LBottomRightTopRight'])"></div>
      </div>

      <div class="LBottomLeft"                      onmouseover="addHighlight(['LBottomLeft'])" onmouseout="removeHighlight(['LBottomLeft'])">
        <div class="LBottomLeftTopLeft area-overlap cell" onmouseover="addHighlight(['LBottomLeft', 'LTopLeft']); addCellHighlight([ 'LTopLeftBottomLeft', 'LBottomLeftTopLeft'])" onmouseout="removeHighlight(['LBottomLeft', 'LTopLeft']); removeCellHighlight(['LTopLeftBottomLeft', 'LBottomLeftTopLeft'])"></div>
        <div class="LBottomLeftTopRight cell"          onmouseover="addHighlight(['LTopRight', 'LBottomRight', 'LBottomLeft', 'LTopLeft']); addCellHighlight([ 'LTopLeftBottomRight', 'LTopRightBottomLeft', 'LBottomLeftTopRight', 'LBottomRightTopLeft'])" onmouseout="removeHighlight(['LTopRight', 'LBottomRight', 'LBottomLeft', 'LTopLeft']); removeCellHighlight(['LTopLeftBottomRight', 'LTopRightBottomLeft', 'LBottomLeftTopRight', 'LBottomRightTopLeft'])"></div>
        <div class="LBottomLeftBottomLeft cell"        onmouseover="addHighlight(['LBottomLeftBottomLeft']); addCellHighlight(['LBottomLeftBottomLeft'])" onmouseout="removeHighlight(['LBottomLeftBottomLeft']); removeCellHighlight(['LBottomLeftBottomLeft'])"></div>
        <div class="LBottomLeftBottomRight cell"       onmouseover="addHighlight(['LBottomLeft', 'LBottomRight']); addCellHighlight(['LBottomRightBottomLeft', 'LBottomLeftBottomRight'])" onmouseout="removeHighlight(['LBottomLeft', 'LBottomRight']); removeCellHighlight([ 'LBottomRightBottomLeft', 'LBottomLeftBottomRight'])"></div>
      </div>

      <div class="LBottomRight"                      onmouseover="addHighlight(['LBottomRight'])" onmouseout="removeHighlight(['LBottomRight'])">
        <div class="LBottomRightTopLeft area-overlap cell" onmouseover="addHighlight(['LTopRight', 'LBottomRight', 'LBottomLeft', 'LTopLeft']); addCellHighlight([ 'LTopLeftBottomRight', 'LTopRightBottomLeft', 'LBottomLeftTopRight', 'LBottomRightTopLeft'])" onmouseout="removeHighlight(['LTopRight', 'LBottomRight', 'LBottomLeft', 'LTopLeft']); removeCellHighlight(['LTopLeftBottomRight', 'LTopRightBottomLeft', 'LBottomLeftTopRight', 'LBottomRightTopLeft'])"></div>
        <div class="LBottomRightTopRight cell"          onmouseover="addHighlight(['LBottomRight', 'LTopRight']); addCellHighlight(['LTopRightBottomRight', 'LBottomRightTopRight'])" onmouseout="removeHighlight(['LBottomRight', 'LTopRight']); removeCellHighlight(['LTopRightBottomRight', 'LBottomRightTopRight'])"></div>
        <div class="LBottomRightBottomLeft cell"        onmouseover="addHighlight(['LBottomLeft', 'LBottomRight']); addCellHighlight(['LBottomRightBottomLeft', 'LBottomLeftBottomRight'])" onmouseout="removeHighlight(['LBottomLeft', 'LBottomRight']); removeCellHighlight([ 'LBottomRightBottomLeft', 'LBottomLeftBottomRight'])"></div>
        <div class="LBottomRightBottomRight cell"       onmouseover="addHighlight(['LBottomRightBottomRight']); addCellHighlight(['LBottomRightBottomRight'])" onmouseout="removeHighlight(['LBottomRightBottomRight']); removeCellHighlight(['LBottomRightBottomRight'])"></div>
      </div>
    </div>

<!-- Right Monitor Pallete -->
    <div class="monitor monitor-right" onclick="performAction()" onmouseover="addExpand(['monitor-right']);" onmouseout="removeExpand(['monitor-right']);">
      <div class="RTopLeft"                         onmouseover="addHighlight(['RTopLeft']);" onmouseout="removeHighlight(['RTopLeft']);">
        <div class="RTopLeftTopLeft area-overlap cell" onmouseover="addHighlight(['RTopLeftTopLeft']); addCellHighlight(['RTopLeftTopLeft'])" onmouseout="removeHighlight(['RTopLeftTopLeft']); removeCellHighlight(['RTopLeftTopLeft'])"></div>
        <div class="RTopLeftBottomLeft cell"           onmouseover="addHighlight(['RBottomLeft', 'RTopLeft']); addCellHighlight(['RTopLeftBottomLeft', 'RBottomLeftTopLeft'])" onmouseout="removeHighlight(['RBottomLeft', 'RTopLeft']); removeCellHighlight([ 'RTopLeftBottomLeft', 'RBottomLeftTopLeft'])"></div>
        <div class="RTopLeftTopRight cell"             onmouseover="addHighlight(['RTopRight', 'RTopLeft']); addCellHighlight(['RTopLeftTopRight', 'RTopRightTopLeft']);" onmouseout="removeHighlight(['RTopRight', 'RTopLeft']); removeCellHighlight([ 'RTopLeftTopRight', 'RTopRightTopLeft'])"></div>
        <div class="RTopLeftBottomRight cell"          onmouseover="addHighlight(['RTopRight', 'RBottomRight', 'RBottomLeft', 'RTopLeft']); addCellHighlight(['RTopLeftBottomRight', 'RTopRightBottomLeft', 'RBottomLeftTopRight', 'RBottomRightTopLeft'])" onmouseout="removeHighlight(['RTopRight', 'RBottomRight', 'RBottomLeft', 'RTopLeft']); removeCellHighlight(['RTopLeftBottomRight', 'RTopRightBottomLeft', 'RBottomLeftTopRight', 'RBottomRightTopLeft'])"></div>
      </div>

      <div class="RTopRight"                        onmouseover="addHighlight(['RTopRight'])" onmouseout="removeHighlight(['RTopRight'])">
        <div class="RTopRightTopLeft area-overlap cell" onmouseover="addHighlight(['RTopRight', 'RTopLeft']); addCellHighlight(['RTopLeftTopRight', 'RTopRightTopLeft'])" onmouseout="removeHighlight(['RTopRight', 'RTopLeft']); removeCellHighlight(['RTopLeftTopRight', 'RTopRightTopLeft'])"></div>
        <div class="RTopRightTopRight cell"            onmouseover="addHighlight(['RTopRightTopRight']); addCellHighlight(['RTopRightTopRight'])" onmouseout="removeHighlight(['RTopRightTopRight']); removeCellHighlight(['RTopRightTopRight'])"></div>
        <div class="RTopRightBottomLeft cell"          onmouseover="addHighlight(['RTopRight', 'RBottomRight', 'RBottomLeft', 'RTopLeft']); addCellHighlight(['RTopLeftBottomRight', 'RTopRightBottomLeft', 'RBottomLeftTopRight', 'RBottomRightTopLeft'])" onmouseout="removeHighlight(['RTopRight', 'RBottomRight', 'RBottomLeft', 'RTopLeft']); removeCellHighlight(['RTopLeftBottomRight', 'RTopRightBottomLeft', 'RBottomLeftTopRight', 'RBottomRightTopLeft'])"></div>
        <div class="RTopRightBottomRight cell"         onmouseover="addHighlight(['RTopRight', 'RBottomRight']); addCellHighlight(['RTopRightBottomRight', 'RBottomRightTopRight'])" onmouseout="removeHighlight(['RTopRight', 'RBottomRight']); removeCellHighlight(['RTopRightBottomRight', 'RBottomRightTopRight'])"></div>
      </div>

      <div class="RBottomLeft"                      onmouseover="addHighlight(['RBottomLeft'])" onmouseout="removeHighlight(['RBottomLeft'])">
        <div class="RBottomLeftTopLeft area-overlap cell" onmouseover="addHighlight(['RBottomLeft', 'RTopLeft']); addCellHighlight([ 'RTopLeftBottomLeft', 'RBottomLeftTopLeft'])" onmouseout="removeHighlight(['BottomLeft', 'RTopLeft']); removeCellHighlight(['RTopLeftBottomLeft', 'RBottomLeftTopLeft'])"></div>
        <div class="RBottomLeftTopRight cell"          onmouseover="addHighlight(['RTopRight', 'RBottomRight', 'RBottomLeft', 'RTopLeft']); addCellHighlight([ 'RTopLeftBottomRight', 'RTopRightBottomLeft', 'RBottomLeftTopRight', 'RBottomRightTopLeft'])" onmouseout="removeHighlight(['RTopRight', 'RBottomRight', 'RBottomLeft', 'RTopLeft']); removeCellHighlight(['RTopLeftBottomRight', 'RTopRightBottomLeft', 'RBottomLeftTopRight', 'RBottomRightTopLeft'])"></div>
        <div class="RBottomLeftBottomLeft cell"        onmouseover="addHighlight(['RBottomLeftBottomLeft']); addCellHighlight(['RBottomLeftBottomLeft'])" onmouseout="removeHighlight(['RBottomLeftBottomLeft']); removeCellHighlight(['RBottomLeftBottomLeft'])"></div>
        <div class="RBottomLeftBottomRight cell"       onmouseover="addHighlight(['RBottomLeft', 'RBottomRight']); addCellHighlight(['RBottomRightBottomLeft', 'RBottomLeftBottomRight'])" onmouseout="removeHighlight(['RBottomLeft', 'RBottomRight']); removeCellHighlight([ 'RBottomRightBottomLeft', 'RBottomLeftBottomRight'])"></div>
      </div>

      <div class="RBottomRight"                      onmouseover="addHighlight(['RBottomRight'])" onmouseout="removeHighlight(['RBottomRight'])">
        <div class="RBottomRightTopLeft area-overlap cell" onmouseover="addHighlight(['RTopRight', 'RBottomRight', 'RBottomLeft', 'RTopLeft']); addCellHighlight([ 'RTopLeftBottomRight', 'RTopRightBottomLeft', 'RBottomLeftTopRight', 'RBottomRightTopLeft'])" onmouseout="removeHighlight(['RTopRight', 'RBottomRight', 'RBottomLeft', 'RTopLeft']); removeCellHighlight(['RTopLeftBottomRight', 'RTopRightBottomLeft', 'RBottomLeftTopRight', 'RBottomRightTopLeft'])"></div>
        <div class="RBottomRightTopRight cell"          onmouseover="addHighlight(['RBottomRight', 'RTopRight']); addCellHighlight(['RTopRightBottomRight', 'RBottomRightTopRight'])" onmouseout="removeHighlight(['RBottomRight', 'RTopRight']); removeCellHighlight(['RTopRightBottomRight', 'RBottomRightTopRight'])"></div>
        <div class="RBottomRightBottomLeft cell"        onmouseover="addHighlight(['RBottomLeft', 'RBottomRight']); addCellHighlight(['RBottomRightBottomLeft', 'RBottomLeftBottomRight'])" onmouseout="removeHighlight(['RBottomLeft', 'RBottomRight']); removeCellHighlight([ 'RBottomRightBottomLeft', 'RBottomLeftBottomRight'])"></div>
        <div class="RBottomRightBottomRight cell"       onmouseover="addHighlight(['RBottomRightBottomRight']); addCellHighlight(['RBottomRightBottomRight'])" onmouseout="removeHighlight(['RBottomRightBottomRight']); removeCellHighlight(['RBottomRightBottomRight'])"></div>
      </div>
    </div>

<!-- Current Monitor Pallete -->
    <div class="grid-container" onclick="performAction()">
      <div class="TopLeft"                         onmouseover="addHighlight(['TopLeft']);" onmouseout="removeHighlight(['TopLeft']);">
        <div class="TopLeftTopLeft area-overlap cell" onmouseover="addHighlight(['TopLeftTopLeft']); addCellHighlight(['TopLeftTopLeft'])" onmouseout="removeHighlight(['TopLeftTopLeft']); removeCellHighlight(['TopLeftTopLeft'])"></div>
        <div class="TopLeftBottomLeft cell"           onmouseover="addHighlight(['BottomLeft', 'TopLeft']); addCellHighlight(['TopLeftBottomLeft', 'BottomLeftTopLeft'])" onmouseout="removeHighlight(['BottomLeft', 'TopLeft']); removeCellHighlight([ 'TopLeftBottomLeft', 'BottomLeftTopLeft'])"></div>
        <div class="TopLeftTopRight cell"             onmouseover="addHighlight(['TopRight', 'TopLeft']); addCellHighlight(['TopLeftTopRight', 'TopRightTopLeft']);" onmouseout="removeHighlight(['TopRight', 'TopLeft']); removeCellHighlight(['TopLeftTopRight', 'TopRightTopLeft'])"></div>
        <div class="TopLeftBottomRight cell"          onmouseover="addHighlight(['TopRight', 'BottomRight', 'BottomLeft', 'TopLeft']); addCellHighlight(['TopLeftBottomRight', 'TopRightBottomLeft', 'BottomLeftTopRight', 'BottomRightTopLeft'])" onmouseout="removeHighlight(['TopRight', 'BottomRight', 'BottomLeft', 'TopLeft']); removeCellHighlight(['TopLeftBottomRight', 'TopRightBottomLeft', 'BottomLeftTopRight', 'BottomRightTopLeft'])"></div>
      </div>

      <div class="TopRight"                        onmouseover="addHighlight(['TopRight'])" onmouseout="removeHighlight(['TopRight'])">
        <div class="TopRightTopLeft area-overlap cell"onmouseover="addHighlight(['TopRight', 'TopLeft']); addCellHighlight([ 'TopLeftTopRight', 'TopRightTopLeft'])" onmouseout="removeHighlight(['TopRight', 'TopLeft']); removeCellHighlight(['TopLeftTopRight', 'TopRightTopLeft'])"></div>
        <div class="TopRightTopRight cell"            onmouseover="addHighlight(['TopRightTopRight']); addCellHighlight(['TopRightTopRight'])" onmouseout="removeHighlight(['TopRightTopRight']); removeCellHighlight(['TopRightTopRight'])"></div>
        <div class="TopRightBottomLeft cell"          onmouseover="addHighlight(['TopRight', 'BottomRight', 'BottomLeft', 'TopLeft']); addCellHighlight(['TopLeftBottomRight', 'TopRightBottomLeft', 'BottomLeftTopRight', 'BottomRightTopLeft'])" onmouseout="removeHighlight(['TopRight', 'BottomRight', 'BottomLeft', 'TopLeft']); removeCellHighlight(['TopLeftBottomRight', 'TopRightBottomLeft', 'BottomLeftTopRight', 'BottomRightTopLeft'])"></div>
        <div class="TopRightBottomRight cell"         onmouseover="addHighlight(['TopRight', 'BottomRight']); addCellHighlight(['TopRightBottomRight', 'BottomRightTopRight'])" onmouseout="removeHighlight(['TopRight', 'BottomRight']); removeCellHighlight(['TopRightBottomRight', 'BottomRightTopRight'])"></div>
      </div>

      <div class="BottomLeft"                      onmouseover="addHighlight(['BottomLeft'])" onmouseout="removeHighlight(['BottomLeft'])">
        <div class="BottomLeftTopLeft area-overlap cell" onmouseover="addHighlight(['BottomLeft', 'TopLeft']); addCellHighlight([ 'TopLeftBottomLeft', 'BottomLeftTopLeft'])" onmouseout="removeHighlight(['BottomLeft', 'TopLeft']); removeCellHighlight(['TopLeftBottomLeft', 'BottomLeftTopLeft'])"></div>
        <div class="BottomLeftTopRight cell"          onmouseover="addHighlight(['TopRight', 'BottomRight', 'BottomLeft', 'TopLeft']); addCellHighlight([ 'TopLeftBottomRight', 'TopRightBottomLeft', 'BottomLeftTopRight', 'BottomRightTopLeft'])" onmouseout="removeHighlight(['TopRight', 'BottomRight', 'BottomLeft', 'TopLeft']); removeCellHighlight(['TopLeftBottomRight', 'TopRightBottomLeft', 'BottomLeftTopRight', 'BottomRightTopLeft'])"></div>
        <div class="BottomLeftBottomLeft cell"        onmouseover="addHighlight(['BottomLeftBottomLeft']); addCellHighlight(['BottomLeftBottomLeft'])" onmouseout="removeHighlight(['BottomLeftBottomLeft']); removeCellHighlight(['BottomLeftBottomLeft'])"></div>
        <div class="BottomLeftBottomRight cell"       onmouseover="addHighlight(['BottomLeft', 'BottomRight']); addCellHighlight(['BottomRightBottomLeft', 'BottomLeftBottomRight'])" onmouseout="removeHighlight(['BottomLeft', 'BottomRight']); removeCellHighlight([ 'BottomRightBottomLeft', 'BottomLeftBottomRight'])"></div>
      </div>

      <div class="BottomRight"                      onmouseover="addHighlight(['BottomRight'])" onmouseout="removeHighlight(['BottomRight'])">
        <div class="BottomRightTopLeft cell" onmouseover="addHighlight(['TopRight', 'BottomRight', 'BottomLeft', 'TopLeft']); addCellHighlight([ 'TopLeftBottomRight', 'TopRightBottomLeft', 'BottomLeftTopRight', 'BottomRightTopLeft'])" onmouseout="removeHighlight(['TopRight', 'BottomRight', 'BottomLeft', 'TopLeft']); removeCellHighlight(['TopLeftBottomRight', 'TopRightBottomLeft', 'BottomLeftTopRight', 'BottomRightTopLeft'])"></div>
        <div class="BottomRightTopRight cell"          onmouseover="addHighlight(['BottomRight', 'TopRight']); addCellHighlight(['TopRightBottomRight', 'BottomRightTopRight'])" onmouseout="removeHighlight(['BottomRight', 'TopRight']); removeCellHighlight(['TopRightBottomRight', 'BottomRightTopRight'])"></div>
        <div class="BottomRightBottomLeft cell"        onmouseover="addHighlight(['BottomLeft', 'BottomRight']); addCellHighlight(['BottomRightBottomLeft', 'BottomLeftBottomRight'])" onmouseout="removeHighlight(['BottomLeft', 'BottomRight']); removeCellHighlight([ 'BottomRightBottomLeft', 'BottomLeftBottomRight'])"></div>
        <div class="BottomRightBottomRight cell"       onmouseover="addHighlight(['BottomRightBottomRight']); addCellHighlight(['BottomRightBottomRight'])" onmouseout="removeHighlight(['BottomRightBottomRight']); removeCellHighlight(['BottomRightBottomRight'])"></div>
      </div>
    </div>

  </div>
</body>
</html>

Multi-Monitor Visual Window Snapping

is pretty much usable now!

Use the normal command-shift W to bring it up. It will automatically show the expanded menu if a display is connected.

The visuals are still rough around the corners, but it all seems to be working fine.

I haven't tested this on a setup with more than three displays so it would be great if someone could do that and comment on how it behaves!

2 Likes

Oh wow, this is quite a bit more complicated than before! Really cool stuff. Gonna dig into this at random times over the course of the day, not sure when I'll have my next version posted here but as soon as I'm done playing around I'll put it up :smiley:

1 Like

Not having much luck with two monitors over here? My setup is arranged so my monitors are actually in line? (laptop screen in front / below desktop monitor). If I change the arrangement so they're side by side, I can get things to move from the laptop to the desktop screen, but not back. Any ideas?

It seems like it only works for horizontal setups... I guess you could go through the 'other' triggers and set the left one to down, and the right one to up, etc. as a temporary fix

It would be good to support vertical layouts but I don't know how to detect where each display is, only how many have been connected.

@Andreas_Hegenberg, is there a way to detect where the displays are? Also relative to the mouse cursor too

Detecting this stuff is pretty complicated and there are tons of edge cases.
I'm already very impressed by this preset and would probably not try to add that in. If necessary it may make more sense to have a separate one for vertical setups. (Of course everything is possible, Apple Script would allow you to get the positions of the screens, with that info you COULD change the setup, but really it gets complicated very fast)

1 Like

I guess the most stable solution would be to have a user-toggleable setting that switches it between a vertical layout or a horizontal layout, or both if you have some kinda insane display grid.

I'll be focusing on my own preset next and trying to get a stable release up (as it's never had an official stable release) so..... @GoldenChaos?? :wink:

Just thinking of a simple BTT variable and a UI that toggles it. The "Decide Window Snap Menu" will have an added rule to read that variable.

It would also be great if the code could be minimised, especially the classes. (I don't have much experience in HTML!)

Been working on this, actually. I also ran into monitor support issues, so I’m gonna try seeing if I can at least get the basics of one, two, and three monitors arranged horizontally working correctly :slight_smile: seems like right now even the single-monitor detection isn’t working.

Additionally, it doesn’t seem to know which monitor you’re actually using. So the center control right now always snaps to the current monitor, which is more than a little confusing. I think that, minimally, needs to be fixed for sure. Ultimately it may not be a good UX to show three window snappers - perhaps, for now, we change the added window snappers to more generic “move to screen on left/right” chevrons, and have it move the window as-is to the display left or right of the current one. Of course, this would only support moving one monitor at a time, and only horizontally. Hmm.

I’ll work on new class names/trigger names, since I was thinking of changing those anyway, but those are pretty arbitrary and subjective so they’re not “wrong”, even if they’re not super pretty looking!

Finally, I want to tighten up the animations since they’re a bit slow for my tastes. Another subjective thing, though, haha.

1 Like

Yes I never found out how to detect where and what display it was on, only how many displays are connnected to show/hide the extra menus.

I'll let the more experienced person do what's better :wink:

At this point it’s probably fair to say that you’re more experienced with the actual monitor detection, hah. :smile: since I’ve yet to ever look at it...

I gotta make sure I’m not stacking my plate too high. I don’t want to keep you waiting, so I’ll try and prioritize this.

No no i'm not waiting~ Take as much time as you like :smiley:

Just excited to see the changes however long they're gonna take, as I pretty much have no idea how to make it any better myself right now ^^

1 Like

I've finally been getting around to this :slight_smile:

Coming in the next GC build:

2 Likes

Nice! Would it be possible to post an isolated view here?

Sorry this took so long! Here's the isolated window snapper :smiley:

Visual Window Snapper.bttpresetcompressed (16.3 KB)

2 Likes

Hi, I'm trying out your preset with a dual-screen setup. Monitor on the "left" and laptop screen on the "right".

After moving a window to the laptop screen on the right, clicking left doesn't move it back to the monitor on the left.

Seems like it chooses where to move the window based on the monitor your cursor is on.

@Andreas_Hegenberg is there an option to have it move the window based on the window's position and not the cursor's?

Oh wow, this is lit

2 Likes

I decide to made a catalina inspired window manager after using this, i will share preset and icons after some use, thx for inspiration :wink:
Screen Shot 2019-12-28 at 02.11.45

3 Likes