commit 6d9e0b3ef4248f524af5f727aa2168f0a005651e
Author: GayLord <coolperson@email.com>
Date:   Sat Oct 19 17:24:11 2024 +0200

    Initial Commit

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..55c7354
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/.etc
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..dcaa9c3
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+MIT License
+
+Copyright (c) 2024 UwU
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..817ff7b
--- /dev/null
+++ b/README.md
@@ -0,0 +1,4 @@
+# Installation
+Very straightforward, have fun!
+
+
diff --git a/public/assets/css/theme-haxxor.css b/public/assets/css/theme-haxxor.css
new file mode 100644
index 0000000..2c7f9d7
--- /dev/null
+++ b/public/assets/css/theme-haxxor.css
@@ -0,0 +1,304 @@
+:root {
+  --is-haxxorman: true;
+  --is-dark-theme: true;
+  --color-primary: #54a13c;
+  --color-primary-contrast: rgb(115, 222, 82);
+  --color-primary-dark-1: #82ad74;
+  --color-primary-dark-2: #679cd0;
+  --color-primary-dark-3: #7aa8d6;
+  --color-primary-dark-4: #8db5dc;
+  --color-primary-dark-5: #b3cde7;
+  --color-primary-dark-6: #d9e6f3;
+  --color-primary-dark-7: #f4f8fb;
+  --color-primary-light-1: #3876b3;
+  --color-primary-light-2: #31699f;
+  --color-primary-light-3: #2b5c8b;
+  --color-primary-light-4: #254f77;
+  --color-primary-light-5: #193450;
+  --color-primary-light-6: #0c1a28;
+  --color-primary-light-7: #04080c;
+  --color-primary-alpha-10: #4183c419;
+  --color-primary-alpha-20: #4183c433;
+  --color-primary-alpha-30: #4183c44b;
+  --color-primary-alpha-40: #4183c466;
+  --color-primary-alpha-50: #4183c480;
+  --color-primary-alpha-60: #4183c499;
+  --color-primary-alpha-70: #4183c4b3;
+  --color-primary-alpha-80: #4183c4cc;
+  --color-primary-alpha-90: #4183c4e1;
+  --color-primary-hover: #172d11;
+  --color-primary-active: #172d11;
+  --color-secondary: #224019;
+  --color-secondary-dark-1: #294c1e;
+  --color-secondary-dark-2: #284a1d;
+  --color-secondary-dark-3: #2b5120;
+  --color-secondary-dark-4: #2d5521;
+  --color-secondary-dark-5: #305923;
+  --color-secondary-dark-6: #335e26;
+  --color-secondary-dark-7: #356428;
+  --color-secondary-dark-8: #3e762f;
+  --color-secondary-dark-9: #3c712d;
+  --color-secondary-dark-10: #b7bfc7;
+  --color-secondary-dark-11: #c5cbd2;
+  --color-secondary-dark-12: #cfd4da;
+  --color-secondary-dark-13: #d2d7dc;
+  --color-secondary-light-1: #313940;
+  --color-secondary-light-2: #292f35;
+  --color-secondary-light-3: #1d2226;
+  --color-secondary-light-4: #171b1e;
+  --color-secondary-alpha-10: #3b444c19;
+  --color-secondary-alpha-20: #3b444c33;
+  --color-secondary-alpha-30: #3b444c4b;
+  --color-secondary-alpha-40: #3b444c66;
+  --color-secondary-alpha-50: #3b444c80;
+  --color-secondary-alpha-60: #3b444c99;
+  --color-secondary-alpha-70: #3b444cb3;
+  --color-secondary-alpha-80: #3b444ccc;
+  --color-secondary-alpha-90: #3b444ce1;
+  --color-secondary-button: var(--color-secondary-dark-4);
+  --color-secondary-hover: var(--color-secondary-dark-3);
+  --color-secondary-active: var(--color-secondary-dark-2);
+  /* console colors - used for actions console and console files */
+  --color-console-fg: #f7f8f9;
+  --color-console-fg-subtle: #bdc4cc;
+  --color-console-bg: #171b1e;
+  --color-console-border: #2e353b;
+  --color-console-hover-bg: #272d33;
+  --color-console-active-bg: #2e353b;
+  --color-console-menu-bg: #262b31;
+  --color-console-menu-border: #414b55;
+  /* named colors */
+  --color-red: #cc4848;
+  --color-orange: #cc580c;
+  --color-yellow: #cc9903;
+  --color-olive: #91a313;
+  --color-green: #87ab63;
+  --color-teal: #00918a;
+  --color-blue: #3a8ac6;
+  --color-violet: #906ae1;
+  --color-purple: #b259d0;
+  --color-pink: #d22e8b;
+  --color-brown: #a47252;
+  --color-black: #1d2328;
+  /* light variants - produced via Sass scale-color(color, $lightness: +10%) */
+  --color-red-light: #d15a5a;
+  --color-orange-light: #f6a066;
+  --color-yellow-light: #eaaf03;
+  --color-olive-light: #abc016;
+  --color-green-light: #93b373;
+  --color-teal-light: #00b6ad;
+  --color-blue-light: #4e96cc;
+  --color-violet-light: #9b79e4;
+  --color-purple-light: #ba6ad5;
+  --color-pink-light: #d74397;
+  --color-brown-light: #b08061;
+  --color-black-light: #424851;
+  /* dark 1 variants - produced via Sass scale-color(color, $lightness: -10%) */
+  --color-red-dark-1: #c23636;
+  --color-orange-dark-1: #f38236;
+  --color-yellow-dark-1: #b88a03;
+  --color-olive-dark-1: #839311;
+  --color-green-dark-1: #7a9e55;
+  --color-teal-dark-1: #00837c;
+  --color-blue-dark-1: #347cb3;
+  --color-violet-dark-1: #7b4edb;
+  --color-purple-dark-1: #a742c9;
+  --color-pink-dark-1: #be297d;
+  --color-brown-dark-1: #94674a;
+  --color-black-dark-1: #292e38;
+  /* dark 2 variants - produced via Sass scale-color(color, $lightness: -20%) */
+  --color-red-dark-2: #ad3030;
+  --color-orange-dark-2: #f16e17;
+  --color-yellow-dark-2: #a37a02;
+  --color-olive-dark-2: #74820f;
+  --color-green-dark-2: #6c8c4c;
+  --color-teal-dark-2: #00746e;
+  --color-blue-dark-2: #2e6e9f;
+  --color-violet-dark-2: #6733d6;
+  --color-purple-dark-2: #9834b9;
+  --color-pink-dark-2: #a9246f;
+  --color-brown-dark-2: #835b42;
+  --color-black-dark-2: #272930;
+  /* ansi colors used for actions console and console files */
+  --color-ansi-black: #1e2327;
+  --color-ansi-red: #cc4848;
+  --color-ansi-green: #87ab63;
+  --color-ansi-yellow: #cc9903;
+  --color-ansi-blue: #3a8ac6;
+  --color-ansi-magenta: #d22e8b;
+  --color-ansi-cyan: #00918a;
+  --color-ansi-white: var(--color-console-fg-subtle);
+  --color-ansi-bright-black: #424851;
+  --color-ansi-bright-red: #d15a5a;
+  --color-ansi-bright-green: #93b373;
+  --color-ansi-bright-yellow: #eaaf03;
+  --color-ansi-bright-blue: #4e96cc;
+  --color-ansi-bright-magenta: #d74397;
+  --color-ansi-bright-cyan: #00b6ad;
+  --color-ansi-bright-white: var(--color-console-fg);
+  /* other colors */
+  --color-grey: #384149;
+  --color-grey-light: #818f9e;
+  --color-gold: #b1983b;
+  --color-white: #ffffff;
+  --color-diff-added-linenum-bg: #274227;
+  --color-diff-added-row-bg: #203224;
+  --color-diff-added-row-border: #314a37;
+  --color-diff-added-word-bg: #3c653c;
+  --color-diff-moved-row-bg: #818044;
+  --color-diff-moved-row-border: #bcca6f;
+  --color-diff-removed-linenum-bg: #482121;
+  --color-diff-removed-row-bg: #301e1e;
+  --color-diff-removed-row-border: #634343;
+  --color-diff-removed-word-bg: #6f3333;
+  --color-diff-inactive: #22282d;
+  --color-error-border: #a04141;
+  --color-error-bg: #522;
+  --color-error-bg-active: #744;
+  --color-error-bg-hover: #633;
+  --color-error-text: #f9cbcb;
+  --color-success-border: #458a57;
+  --color-success-bg: #284034;
+  --color-success-text: #6cc664;
+  --color-warning-border: #bb9d00;
+  --color-warning-bg: #3a3a30;
+  --color-warning-text: #fbbd08;
+  --color-info-border: #306090;
+  --color-info-bg: #26354c;
+  --color-info-text: #38a8e8;
+  --color-red-badge: #db2828;
+  --color-red-badge-bg: #db28281a;
+  --color-red-badge-hover-bg: #db28284d;
+  --color-green-badge: #21ba45;
+  --color-green-badge-bg: #21ba451a;
+  --color-green-badge-hover-bg: #21ba454d;
+  --color-yellow-badge: #fbbd08;
+  --color-yellow-badge-bg: #fbbd081a;
+  --color-yellow-badge-hover-bg: #fbbd084d;
+  --color-orange-badge: #f2711c;
+  --color-orange-badge-bg: #f2711c1a;
+  --color-orange-badge-hover-bg: #f2711c4d;
+  --color-git: #f05133;
+  /* target-based colors */
+  --color-body: #000000;
+  --color-box-header: #000000;
+  --color-box-body: #000000;
+  --color-box-body-highlight: #1b1f1a;
+  --color-text-dark: #54a13c;
+  --color-text: #54a13c;
+  --color-text-light: #54a13c;
+  --color-text-light-1: #417d2f;
+  --color-text-light-2: #457636;
+  --color-text-light-3: #234519;
+  --color-footer: #000000;
+  --color-timeline: #343c44;
+  --color-input-text: var(--color-text-dark);
+  --color-input-background: #000000;
+  --color-input-toggle-background: #11210c;
+  --color-input-border: var(--color-secondary);
+  --color-input-border-hover: var(--color-secondary-dark-1);
+  --color-light: #00001728;
+  --color-light-mimic-enabled: rgba(0, 0, 0, calc(40 / 255 * 222 / 255 / var(--opacity-disabled)));
+  --color-light-border: #e8f3ff28;
+  --color-hover: #13240e;
+  --color-active: #101f0c;
+  --color-menu: #000000;
+  --color-card: #000000;
+  --color-markup-table-row: #e8f3ff0f;
+  --color-markup-code-block: #e8f3ff12;
+  --color-markup-code-inline: #e8f3ff28;
+  --color-button: #171a1e;  
+  --color-code-bg: #14171a;
+  --color-shadow: #00001758;
+  --color-secondary-bg: #000;
+  --color-expand-button: #2f363d;
+  --color-placeholder-text: var(--color-text-light-3);
+  --color-editor-line-highlight: var(--color-primary-light-5);
+  --color-project-board-bg: var(--color-secondary-light-2);
+  --color-caret: var(--color-text); /* should ideally be --color-text-dark, see #15651 */
+  --color-reaction-bg: #e8f3ff12;
+  --color-reaction-hover-bg: var(--color-primary-light-4);
+  --color-reaction-active-bg: var(--color-primary-light-5);
+  --color-tooltip-text: #f9fafb;
+  --color-tooltip-bg: #000b17f0;
+  --color-nav-bg: #000000;
+  --color-nav-hover-bg: #0d160a;
+  --color-nav-text: #54a13c;
+  --color-secondary-nav-bg: #000000;
+  --color-label-text: var(--color-text);
+  --color-label-bg: #7282924b;
+  --color-label-hover-bg: #728292a0;
+  --color-label-active-bg: #728292ff;
+  --color-accent: var(--color-primary-light-1);
+  --color-small-accent: var(--color-primary-light-5);
+  --color-highlight-fg: #87651e;
+  --color-highlight-bg: #352c1c;
+  --color-overlay-backdrop: #080808c0;
+  accent-color: #54a13c;
+  color-scheme: dark;
+}
+
+
+.ui.input {
+  animation: glow 800ms ease-out infinite alternate;
+}
+
+@keyframes glow {
+  0% {
+  border-color: #393;
+  box-shadow: 0 0 5px rgba(0,255,0,.2), inset 0 0 5px rgba(0,255,0,.1), 0 2px 0 #000;
+  }	
+  100% {
+  border-color: #6f6;
+  box-shadow: 0 0 20px rgba(0,255,0,.6), inset 0 0 10px rgba(0,255,0,.4), 0 2px 0 #000;
+  }
+}
+
+
+.ui.primary.button {
+  background: #000000
+}
+
+.ui.ui.ui.ui.small.button {
+  background: #000000
+}
+
+
+@keyframes bg-animation {
+    0% { transform: translate(0,0) }
+    10% { transform: translate(-5%,-5%) }
+    20% { transform: translate(-10%,5%) }
+    30% { transform: translate(5%,-10%) }
+    40% { transform: translate(-5%,15%) }
+    50% { transform: translate(-10%,5%) }
+    60% { transform: translate(15%,0) }
+    70% { transform: translate(0,10%) }
+    80% { transform: translate(-15%,0) }
+    90% { transform: translate(10%,5%) }
+    100% { transform: translate(5%,0) }
+}
+
+/* invert emojis that are hard to read otherwise */
+.emoji[aria-label="check mark"],
+.emoji[aria-label="currency exchange"],
+.emoji[aria-label="TOP arrow"],
+.emoji[aria-label="END arrow"],
+.emoji[aria-label="ON! arrow"],
+.emoji[aria-label="SOON arrow"],
+.emoji[aria-label="heavy dollar sign"],
+.emoji[aria-label="copyright"],
+.emoji[aria-label="registered"],
+.emoji[aria-label="trade mark"],
+.emoji[aria-label="multiply"],
+.emoji[aria-label="plus"],
+.emoji[aria-label="minus"],
+.emoji[aria-label="divide"],
+.emoji[aria-label="curly loop"],
+.emoji[aria-label="double curly loop"],
+.emoji[aria-label="wavy dash"],
+.emoji[aria-label="paw prints"],
+.emoji[aria-label="musical note"],
+.emoji[aria-label="musical notes"] {
+  color: #54a13c;
+  /*filter: invert(100%) hue-rotate(180deg);*/
+}
diff --git a/public/assets/scripts/haxxor.js b/public/assets/scripts/haxxor.js
new file mode 100644
index 0000000..ba9fd53
--- /dev/null
+++ b/public/assets/scripts/haxxor.js
@@ -0,0 +1,507 @@
+// Stolen from https://codepen.io/loktar00/pen/BaGqXY
+// However has modifications to work good as a theme
+
+"use strict";
+function createTetris() {
+  var tetrominos = [
+    {
+      // box
+      colors: ["rgb(00, 00, 00)", "#47ba23", "rgb(00, 00, 00)"],
+      data: [
+        [0, 0, 0, 0],
+        [0, 1, 1, 0],
+        [0, 1, 1, 0],
+        [0, 0, 0, 0],
+      ],
+    },
+    {
+      // stick
+      colors: ["rgb(00, 00, 00)", "#47ba23", "rgb(00, 00, 00)"],
+      data: [
+        [0, 0, 0, 0],
+        [0, 0, 0, 0],
+        [1, 1, 1, 1],
+        [0, 0, 0, 0],
+      ],
+    },
+    {
+      // z
+      colors: ["rgb(00, 00, 00)", "#47ba23", "rgb(00, 00, 00)"],
+      data: [
+        [0, 0, 0, 0],
+        [0, 1, 1, 0],
+        [0, 0, 1, 1],
+        [0, 0, 0, 0],
+      ],
+    },
+    {
+      // T
+      colors: ["rgb(00, 00, 00)", "#47ba23", "rgb(00, 00, 00)"],
+      data: [
+        [0, 0, 0, 0],
+        [0, 1, 1, 1],
+        [0, 0, 1, 0],
+        [0, 0, 0, 0],
+      ],
+    },
+    {
+      // s
+      colors: ["rgb(00, 00, 00)", "#47ba23", "rgb(00, 00, 00)"],
+      data: [
+        [0, 0, 0, 0],
+        [0, 1, 1, 0],
+        [1, 1, 0, 0],
+        [0, 0, 0, 0],
+      ],
+    },
+    {
+      // backwards L
+      colors: ["rgb(00, 00, 00)", "#47ba23", "rgb(00, 00, 00)"],
+      data: [
+        [0, 0, 1, 0],
+        [0, 0, 1, 0],
+        [0, 1, 1, 0],
+        [0, 0, 0, 0],
+      ],
+    },
+    {
+      // L
+      colors: ["rgb(00, 00, 00)", "#47ba23", "rgb(00, 00, 00)"],
+      data: [
+        [0, 1, 0, 0],
+        [0, 1, 0, 0],
+        [0, 1, 1, 0],
+        [0, 0, 0, 0],
+      ],
+    },
+  ];
+
+  var Tetris = function (x, y, width, height) {
+    this.posX = x || 0;
+    this.posY = y || 0;
+
+    this.width = width || window.innerWidth;
+    this.height = height || window.innerHeight;
+
+    this.bgCanvas = document.createElement("canvas");
+    this.fgCanvas = document.createElement("canvas");
+
+    this.bgCanvas.width = this.fgCanvas.width = this.width;
+    this.bgCanvas.height = this.fgCanvas.height = this.height;
+
+    this.bgCtx = this.bgCanvas.getContext("2d");
+    this.fgCtx = this.fgCanvas.getContext("2d");
+
+    this.bgCanvas.style.left = this.posX + "px";
+    this.bgCanvas.style.top = this.posY + "px";
+
+    // Stay in background
+    this.bgCanvas.style.position = "absolute";
+    this.fgCanvas.style.position = "absolute";
+
+    this.fgCanvas.style.left = this.posX + "px";
+    this.fgCanvas.style.top = this.posY + "px";
+
+    document.body.insertBefore(this.fgCanvas, document.body.firstChild);
+    document.body.insertBefore(this.bgCanvas, document.body.firstChild);
+    this.init();
+  };
+
+  Tetris.prototype.init = function () {
+    this.curPiece = {
+      data: null,
+      colors: [0, 0, 0],
+      x: 0,
+      y: 0,
+    };
+
+    this.lastMove = Date.now();
+    this.curSpeed = 50 + Math.random() * 50;
+    this.unitSize = 20;
+    this.linesCleared = 0;
+    this.level = 0;
+    this.loseBlock = 0;
+
+    // init the board
+    this.board = [];
+    this.boardWidth = Math.floor(this.width / this.unitSize);
+    this.boardHeight = Math.floor(this.height / this.unitSize);
+
+    var board = this.board,
+      boardWidth = this.boardWidth,
+      boardHeight = this.boardHeight,
+      halfHeight = boardHeight / 2,
+      curPiece = this.curPiece,
+      x = 0,
+      y = 0;
+
+    // init board
+    for (x = 0; x <= boardWidth; x++) {
+      board[x] = [];
+      for (y = 0; y <= boardHeight; y++) {
+        board[x][y] = {
+          data: 0,
+          colors: ["rgb(0,0,0)", "rgb(0,0,0)", "rgb(0,0,0)"],
+        };
+
+        if (Math.random() > 0.15 && y > halfHeight) {
+          board[x][y] = {
+            data: 1,
+            colors:
+              tetrominos[Math.floor(Math.random() * tetrominos.length)].colors,
+          };
+        }
+      }
+    }
+
+    // collapse the board a bit
+    for (x = 0; x <= boardWidth; x++) {
+      for (y = boardHeight - 1; y > -1; y--) {
+        if (board[x][y].data === 0 && y > 0) {
+          for (var yy = y; yy > 0; yy--) {
+            if (board[x][yy - 1].data) {
+              board[x][yy].data = 1;
+              board[x][yy].colors = board[x][yy - 1].colors;
+
+              board[x][yy - 1].data = 0;
+              board[x][yy - 1].colors = [
+                "rgb(0,0,0)",
+                "rgb(0,0,0)",
+                "rgb(0,0,0)",
+              ];
+            }
+          }
+        }
+      }
+    }
+
+    var self = this;
+
+    window.addEventListener("keydown", function (e) {
+      switch (e.keyCode) {
+        case 37:
+          if (self.checkMovement(curPiece, -1, 0)) {
+            curPiece.x--;
+          }
+          break;
+        case 39:
+          if (self.checkMovement(curPiece, 1, 0)) {
+            curPiece.x++;
+          }
+          break;
+        case 40:
+          if (self.checkMovement(curPiece, 0, 1)) {
+            curPiece.y++;
+          }
+          break;
+        case 32:
+        case 38:
+          curPiece.data = self.rotateTetrimono(curPiece);
+          break;
+      }
+    });
+
+    // render the board
+    this.checkLines();
+    this.renderBoard();
+
+    // assign the first tetri
+    this.newTetromino();
+    this.update();
+  };
+
+  Tetris.prototype.update = function () {
+    var curPiece = this.curPiece;
+
+    if (!this.checkMovement(curPiece, 0, 1)) {
+      if (curPiece.y < -1) {
+        // you lose
+        this.loseScreen();
+        return true;
+      } else {
+        this.fillBoard(curPiece);
+        this.newTetromino();
+      }
+    } else {
+      if (Date.now() > this.lastMove) {
+        this.lastMove = Date.now() + this.curSpeed;
+        if (this.checkMovement(curPiece, 0, 1)) {
+          curPiece.y++;
+        } else {
+          this.fillBoard(curPiece);
+          this.newTetromino();
+        }
+      }
+    }
+
+    this.render();
+
+    var self = this;
+    requestAnimationFrame(function () {
+      self.update();
+    });
+  };
+
+  // render only the board.
+  Tetris.prototype.renderBoard = function () {
+    var canvas = this.bgCanvas,
+      ctx = this.bgCtx,
+      unitSize = this.unitSize,
+      board = this.board,
+      boardWidth = this.boardWidth,
+      boardHeight = this.boardHeight;
+
+    ctx.clearRect(0, 0, canvas.width, canvas.height);
+    for (var x = 0; x <= boardWidth; x++) {
+      for (var y = 0; y <= boardHeight; y++) {
+        if (board[x][y].data !== 0) {
+          var bX = x * unitSize,
+            bY = y * unitSize;
+          //ctx.shadowBlur = 5;
+          //ctx.shadowColor = "rgba(0,255,0,.6)";
+          ctx.fillStyle = board[x][y].colors[0];
+          ctx.fillRect(bX, bY, unitSize, unitSize);
+
+          ctx.fillStyle = board[x][y].colors[1];
+          ctx.fillRect(bX + 2, bY + 2, unitSize - 4, unitSize - 4);
+
+          ctx.fillStyle = board[x][y].colors[2];
+          ctx.fillRect(bX + 4, bY + 4, unitSize - 8, unitSize - 8);
+        }
+      }
+    }
+  };
+
+  // Render the current active piece
+  Tetris.prototype.render = function () {
+    var canvas = this.fgCanvas,
+      ctx = this.fgCtx,
+      unitSize = this.unitSize,
+      curPiece = this.curPiece;
+
+    ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+    for (var x = 0; x < 4; x++) {
+      for (var y = 0; y < 4; y++) {
+        if (curPiece.data[x][y] === 1) {
+          var xPos = (curPiece.x + x) * unitSize,
+            yPos = (curPiece.y + y) * unitSize;
+
+          if (yPos > -1) {
+            ctx.shadowBlur = 10;
+            ctx.shadowColor = "rgba(0,255,0,.6)";
+            ctx.fillStyle = curPiece.colors[0];
+            ctx.fillRect(xPos, yPos, unitSize, unitSize);
+
+            ctx.fillStyle = curPiece.colors[1];
+            ctx.fillRect(xPos + 2, yPos + 2, unitSize - 4, unitSize - 4);
+
+            ctx.fillStyle = curPiece.colors[2];
+            ctx.fillRect(xPos + 4, yPos + 4, unitSize - 8, unitSize - 8);
+          }
+        }
+      }
+    }
+  };
+
+  // Make sure we can mov where we want.
+  Tetris.prototype.checkMovement = function (curPiece, newX, newY) {
+    var piece = curPiece.data,
+      posX = curPiece.x,
+      posY = curPiece.y,
+      board = this.board,
+      boardWidth = this.boardWidth,
+      boardHeight = this.boardHeight;
+
+    for (var x = 0; x < 4; x++) {
+      for (var y = 0; y < 4; y++) {
+        if (piece[x][y] === 1) {
+          if (!board[posX + x + newX]) {
+            board[posX + x + newX] = [];
+          }
+
+          if (!board[posX + x + newX][y + posY + newY]) {
+            board[posX + x + newX][y + posY + newY] = {
+              data: 0,
+            };
+          }
+
+          if (
+            posX + x + newX >= boardWidth ||
+            posX + x + newX < 0 ||
+            board[posX + x + newX][y + posY + newY].data == 1
+          ) {
+            return false;
+          }
+
+          if (posY + y + newY > boardHeight) {
+            return false;
+          }
+        }
+      }
+    }
+    return true;
+  };
+
+  // checks for completed lines and clears them
+  Tetris.prototype.checkLines = function () {
+    var board = this.board,
+      boardWidth = this.boardWidth,
+      boardHeight = this.boardHeight,
+      linesCleared = this.linesCleared,
+      level = this.level,
+      y = boardHeight + 1;
+
+    while (y--) {
+      var x = boardWidth,
+        lines = 0;
+
+      while (x--) {
+        if (board[x][y].data === 1) {
+          lines++;
+        }
+      }
+
+      if (lines === boardWidth) {
+        linesCleared++;
+        level = Math.round(linesCleared / 20) * 20;
+
+        var lineY = y;
+        while (lineY) {
+          for (x = 0; x <= boardWidth; x++) {
+            if (lineY - 1 > 0) {
+              board[x][lineY].data = board[x][lineY - 1].data;
+              board[x][lineY].colors = board[x][lineY - 1].colors;
+            }
+          }
+          lineY--;
+        }
+        y++;
+      }
+    }
+  };
+
+  // Lose animation
+  Tetris.prototype.loseScreen = function () {
+    var ctx = this.bgCtx,
+      unitSize = this.unitSize,
+      boardWidth = this.boardWidth,
+      boardHeight = this.boardHeight,
+      y = boardHeight - this.loseBlock;
+
+    for (var x = 0; x < boardWidth; x++) {
+      var bX = x * unitSize,
+        bY = y * unitSize;
+
+      ctx.fillStyle = "rgb(0,0,0)";
+      ctx.fillRect(bX, bY, unitSize, unitSize);
+
+      ctx.fillStyle = "#54a13c";
+      ctx.fillRect(bX + 2, bY + 2, unitSize - 4, unitSize - 4);
+
+      ctx.fillStyle = "rgb(0,0,0)";
+      ctx.fillRect(bX + 4, bY + 4, unitSize - 8, unitSize - 8);
+    }
+
+    if (this.loseBlock <= boardHeight + 1) {
+      this.loseBlock++;
+
+      var self = this;
+      requestAnimationFrame(function () {
+        self.loseScreen();
+      });
+    } else {
+      this.init();
+    }
+  };
+
+  // adds the piece as part of the board
+  Tetris.prototype.fillBoard = function (curPiece) {
+    var piece = curPiece.data,
+      posX = curPiece.x,
+      posY = curPiece.y,
+      board = this.board;
+
+    for (var x = 0; x < 4; x++) {
+      for (var y = 0; y < 4; y++) {
+        if (piece[x][y] === 1) {
+          board[x + posX][y + posY].data = 1;
+          board[x + posX][y + posY].colors = curPiece.colors;
+        }
+      }
+    }
+
+    this.checkLines();
+    this.renderBoard();
+  };
+
+  // rotate a piece
+  Tetris.prototype.rotateTetrimono = function (curPiece) {
+    var rotated = [];
+
+    for (var x = 0; x < 4; x++) {
+      rotated[x] = [];
+      for (var y = 0; y < 4; y++) {
+        rotated[x][y] = curPiece.data[3 - y][x];
+      }
+    }
+
+    if (
+      !this.checkMovement(
+        {
+          data: rotated,
+          x: curPiece.x,
+          y: curPiece.y,
+        },
+        0,
+        0
+      )
+    ) {
+      rotated = curPiece.data;
+    }
+
+    return rotated;
+  };
+
+  // assign the player a new peice
+  Tetris.prototype.newTetromino = function () {
+    var pieceNum = Math.floor(Math.random() * tetrominos.length),
+      curPiece = this.curPiece;
+
+    curPiece.data = tetrominos[pieceNum].data;
+    curPiece.colors = tetrominos[pieceNum].colors;
+    curPiece.x = Math.floor(
+      Math.random() * (this.boardWidth - curPiece.data.length + 1)
+    );
+    curPiece.y = -4;
+  };
+
+  var width = window.innerWidth,
+    boardDiv = 20 * Math.round(window.innerWidth / 20),
+    boards = 8,
+    bWidth = boardDiv / boards,
+    tetrisInstances = [];
+
+  for (var w = 0; w < boards; w++) {
+    tetrisInstances.push(
+      new Tetris(20 * Math.round((w * bWidth) / 20), 0, bWidth)
+    );
+  }
+}
+
+
+
+function main() {
+    if (!isActive()) {
+        return;
+    }
+
+    createTetris();
+}
+
+function isActive() {
+    let style = getComputedStyle(document.body);
+    return style.getPropertyValue("--is-haxxorman") == "true"
+}
+
+document.addEventListener("DOMContentLoaded", main);
\ No newline at end of file
diff --git a/public/assets/templates/custom/header.tmpl b/public/assets/templates/custom/header.tmpl
new file mode 100644
index 0000000..429023c
--- /dev/null
+++ b/public/assets/templates/custom/header.tmpl
@@ -0,0 +1 @@
+<script src="{{AppSubUrl}}/assets/scripts/haxxor.js"></script>
\ No newline at end of file