SanuliHacksGUI

Sanuli huijaus Käyttöliittymä

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         SanuliHacksGUI
// @namespace    http://tampermonkey.net/
// @version      2025-05-24
// @description  Sanuli huijaus Käyttöliittymä
// @author       @theyhoppingonme on discord
// @match        https://sanuli.fi/
// @icon         https://www.google.com/s2/favicons?sz=64&domain=sanuli.fi
// @grant        none
// ==/UserScript==
function isMobile() {
    const hasTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
    const smallScreen = window.innerWidth <= 820;
    const isPortrait = window.matchMedia('(orientation: portrait)').matches;

    const userAgentMobile = /Mobi|Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);

    return (hasTouch && smallScreen && isPortrait) || userAgentMobile;
}

function encode(input, key) {
    let encoded = '';
    for (let i = 0; i < input.length; i++) {
        let charCode = input.charCodeAt(i);
        let keyCharCode = key.charCodeAt(i % key.length);
        let shifted = (charCode + keyCharCode) % 256;
        encoded += shifted.toString(16).padStart(2, '0');
    }
    return encoded;
}

function decode(encoded, key) {
    let decoded = '';
    for (let i = 0; i < encoded.length; i += 2) {
        let byte = parseInt(encoded.substr(i, 2), 16);
        let keyCharCode = key.charCodeAt((i/2) % key.length);
        let originalCharCode = (byte - keyCharCode + 256) % 256;
        decoded += String.fromCharCode(originalCharCode);
    }
    return decoded;
}

function main() {
setTimeout(function() {
(() => {
    if (document.location.href == 'https://sanuli.fi/') {
    let opacity = 0.95
    if (localStorage.getItem('gui')) {
      const guid = JSON.parse(localStorage.getItem('gui'));
      if (guid.o != undefined) {
      opacity = guid.o;
        } else {
          localStorage.removeItem('gui');
        }
    }
    const version = 1.4;
    const guiId = 'an-gui';

    if (document.getElementById(guiId)) {
        console.log('GUI already exists.');
        return;
    }
    let elz;
    let data;
    let settings = localStorage.getItem('settings');
    let sjson = JSON.parse(settings);
    let game = `game|"${sjson.current_game_mode}"|"${sjson.current_word_list}"|${sjson.current_word_length}`;
    let json;

    const tabs = [
        { id: 'set', label: 'Aseta', active: true },
        { id: 'toggle', label: 'Kytkimet', active: false },
        { id: 'button', label: 'Napit', active: false },
        { id: 'settings', label: 'Asetukset', active: false }
    ];

    const inputs = [{
            tab: 'set',
            info: 'Aseta putki',
            placeholder: 'Putki',
            buttonText: 'Aseta',
            type: 'number',
            callback: (value) => {
                try {
                    data = localStorage.getItem(game);
                    if (!data) throw new Error('No data found');
                    json = JSON.parse(data);
                    json.streak = Number(value);
                    localStorage.setItem(game, JSON.stringify(json));
                    setTimeout(function() {
                    location.reload();
                    }, 100);
                } catch (e) {
                    console.error('Failed to set streak:', e);
                }
            }

        },
        {
            tab: 'set',
            info: 'Aseta sana',
            placeholder: 'Sana',
            buttonText: 'Aseta',
            type: 'text',
            callback: (value) => {
                try {
                    data = localStorage.getItem(game);
                    if (!data) throw new Error('No data found');
                    json = JSON.parse(data);
                    value = value.toUpperCase();
                    value = value.split('');
                    json.word = value;
                    json.word_length = value.length;
                    localStorage.setItem(game, JSON.stringify(json));
                    setTimeout(function() {
                    location.reload();
                    }, 100);
                } catch (e) {
                    console.error('Failed to set word:', e);
                }
            }
        },
         {
            tab: 'set',
            info: 'Aseta viesti',
            placeholder: 'Viesti',
            buttonText: 'Aseta',
            type: 'text',
            callback: (value) => {
                try {
                    data = localStorage.getItem(game);
                    if (!data) throw new Error('No data found');
                    json = JSON.parse(data);
                    json.message = value;
                    localStorage.setItem(game, JSON.stringify(json));
                    setTimeout(function() {
                    location.reload();
                    }, 100);
                } catch (e) {
                    console.error('Failed to set message:', e);
                }
            }
        },
        {
            tab: 'settings',
            info: 'Aseta sanulin taustaväri (Heksadesimaali)',
            placeholder: 'Esim: #000000',
            buttonText: 'Aseta',
            type: 'text',
            callback: (value) => {
                try {
                    document.body.style.background = value;
                } catch (e) {
                    console.error('Failed to set color:', e);
                }
            }
        },
    ];
    let interval;
    let recent;
    let word;
    const toggles = [{
            tab: 'toggle',
            info: 'Automaattinen arvaus',
            name: 'autoguess',
            onFunc: () => {
                guess();
            },
            offFunc: () => {
                clearInterval(interval);
            }
        },
    ];
    function guess() {
        interval = setInterval(() => {
                        data = localStorage.getItem(game);
                        json = JSON.parse(data);
                        const word = json.word.join('');
                        if (recent != word) {
                        typeWord(word);
                        recent = word;
                        }
                        document.dispatchEvent(enterEvent);
                        document.dispatchEvent(enterEvent);
                 }, 100);
    }
    const buttons = [{
            tab: 'button',
            info: 'Selvitä sana',
            buttonText: 'Selvitä',
            callback: () => {
                data = localStorage.getItem(game);
                json = JSON.parse(data);
                json.message = 'Sana on: ' + json.word.join('');
                localStorage.setItem(game, JSON.stringify(json));
                alert(json.message);
            }
        },
        {
            tab: 'button',
        info: 'Aseta Maksimiputki',
            buttonText: 'Aseta',
            callback: () => {
                data = localStorage.getItem(game);
                    if (!data) throw new Error('No data found');
                    json = JSON.parse(data);
                    json.streak = 0xFFFFFFFF;
                    localStorage.setItem(game, JSON.stringify(json));
                    setTimeout(function() {
                    location.reload();
                    }, 100);
            }
        },
         {
             tab: 'button',
        info: 'Voita peli',
            buttonText: 'Voita',
            callback: () => {
                data = localStorage.getItem(game);
                    if (!data) throw new Error('No data found');
                    json = JSON.parse(data);
                    json.is_winner = true;
                    json.is_guessing = false;
                    json.is_unknown = false;
                    json.streak += 1;
                    localStorage.setItem(game, JSON.stringify(json));
                    setTimeout(function() {
                    location.reload();
                    }, 100);
            }
        },
        {
            tab: 'settings',
            info: 'Tallenna Sanulitiedot',
            buttonText: 'Tallenna',
            callback: () => {
                navigator.clipboard.writeText(encode(localStorage.getItem(game), 'SanuliHacksGUI'))
                    .then(() => {
                alert('Tallennuskoodi on tallennettu leikepöytään.');
                });
            }
        },
        {
            tab: 'settings',
            info: 'Lataa Sanulitiedot',
            buttonText: 'Lataa',
            callback: () => {
                const save = prompt('Anna tallennettu koodi.');
                if (save) {
                    const str = decode(save, 'SanuliHacksGUI');
                    try {
                        JSON.parse(str);
                        localStorage.setItem(game, str);
                        alert('Ladattu!');
                        location.reload();
                    } catch (e) {
                        alert('Virheellistä koodia!');
                    }
                } else {
                  alert('Et vastannut mitään');
                }
            }
        },
    ];

    const sliders = [{
            tab: 'settings',
            info: 'Käyttöjärjestelmän läpinäkyvyys',
            value: opacity,
            step: 100,
            min: 0,
            max: 1,
            callback: (value) => {
                const element = document.getElementById(guiId);
                if (element) {
                 element.style.opacity = value;
                    data = JSON.parse(localStorage.getItem('gui'));
                    data.o = value;
                    localStorage.setItem('gui', JSON.stringify(data));
                }
            }
        }
    ];

    function simulateKeyPress(key) {
  const keyUpper = key.toUpperCase();
  const keyCode = keyUpper.charCodeAt(0);
  const code = key === 'Enter' ? 'Enter' : 'Key' + keyUpper;

  ['keydown', 'keypress', 'keyup'].forEach(type => {
    const event = new KeyboardEvent(type, {
      key: key,
      code: code,
      keyCode: keyCode,
      which: keyCode,
      bubbles: true,
      cancelable: true,
    });
    document.dispatchEvent(event);
  });
}

function typeWord(word, delay = 10) {
  let i = 0;
  const interval = setInterval(() => {
    if (i >= word.length) return clearInterval(interval);
    simulateKeyPress(word[i++]);
  }, delay);
}

const enterEvent = new KeyboardEvent('keydown', {
  key: 'Enter',
  code: 'Enter',
  keyCode: 13,
  which: 13,
  bubbles: true,
});


    const style = document.createElement('style');
    style.textContent = `
#${guiId} {
  position: fixed;
  top: 20px;
  right: 20px;
  width: 320px;
  background: #2b2b2b;
  color: #e0e0e0;
  border-radius: 8px;
  font-family: Arial, sans-serif;
  box-shadow: 0 4px 12px rgba(0,0,0,0.4);
  z-index: 9999;
  overflow: hidden;
  transition: opacity 0.3s ease, transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
  opacity: ${opacity};
}
#${guiId} .gui-header {
  padding: 12px;
  background: #1e1e1e;
  cursor: move;
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-radius: 8px 8px 0 0;
  user-select: none;
}
#${guiId} .gui-title {
  font-weight: bold;
  font-size: 14px;
  transition: color 0.2s ease;
}
#${guiId} .gui-tabs {
  display: flex;
  background: #262626;
  border-bottom: 1px solid #3a3a3a;
}
#${guiId} .gui-tab {
  padding: 10px 15px;
  font-size: 12px;
  cursor: pointer;
  transition: all 0.3s ease;
  user-select: none;
  border-bottom: 2px solid transparent;
  position: relative;
  overflow: hidden;
}
#${guiId} .gui-tab:hover {
  background: #303030;
}
#${guiId} .gui-tab.active {
  background: #2b2b2b;
  border-bottom: 2px solid #4CAF50;
  font-weight: bold;
}

#${guiId} .gui-tab::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 5px;
  height: 5px;
  background: rgba(255, 255, 255, 0.5);
  opacity: 0;
  border-radius: 100%;
  transform: scale(1, 1) translate(-50%);
  transform-origin: 50% 50%;
}
#${guiId} .gui-tab:focus:not(:active)::after {
  animation: ripple 0.5s ease-out;
}
@keyframes ripple {
  0% {
    transform: scale(0, 0);
    opacity: 0.5;
  }
  100% {
    transform: scale(20, 20);
    opacity: 0;
  }
}
#${guiId} .gui-content {
  padding: 16px;
  max-height: 70vh;
  overflow-y: auto;
  transition: max-height 0.4s ease-in-out;
}
#${guiId} .tab-content {
  display: none;
  animation-duration: 0.45s;
  animation-fill-mode: both;
  animation-timing-function: cubic-bezier(0.25, 0.1, 0.25, 1);
}
#${guiId} .tab-content.active {
  display: block;
  animation-name: fadeIn;
}
#${guiId} .tab-content.fade-out {
  animation-name: fadeOut;
}
@keyframes fadeIn {
  0% {
    opacity: 0;
    transform: translateY(10px);
  }
  100% {
    opacity: 1;
    transform: translateY(0);
  }
}
@keyframes fadeOut {
  0% {
    opacity: 1;
    transform: translateY(0);
  }
  100% {
    opacity: 0;
    transform: translateY(-10px);
  }
}
#${guiId} .gui-item {
  margin-bottom: 16px;
  display: flex;
  flex-direction: column;
  transition: transform 0.2s ease-out;
}
#${guiId} .gui-item:hover {
  transform: translateX(2px);
}
#${guiId} .gui-item-info {
  margin-bottom: 6px;
  font-size: 12px;
  color: #b0b0b0;
  transition: color 0.2s ease;
}
#${guiId} .gui-input-container {
  display: flex;
  gap: 8px;
}
#${guiId} input {
  flex: 1;
  padding: 8px;
  background: #3a3a3a;
  border: 1px solid #4a4a4a;
  color: #e0e0e0;
  border-radius: 4px;
  outline: none;
  transition: border-color 0.25s ease, box-shadow 0.25s ease;
}
#${guiId} input:focus {
  border-color: #6a6a6a;
  box-shadow: 0 0 0 2px rgba(106, 106, 106, 0.3);
}
#${guiId} button {
  padding: 8px 12px;
  background: #4a4a4a;
  border: none;
  color: #e0e0e0;
  border-radius: 4px;
  cursor: pointer;
  transition: background 0.25s ease, transform 0.15s ease;
  outline: none;
}
#${guiId} button:hover {
  background: #5a5a5a;
  transform: translateY(-1px);
}
#${guiId} button:active {
  background: #6a6a6a;
  transform: translateY(1px);
}
#${guiId} .toggle-container {
  display: flex;
  align-items: center;
  gap: 8px;
}
#${guiId} .toggle-switch {
  position: relative;
  width: 40px;
  height: 20px;
}
#${guiId} .toggle-slider {
  position: absolute;
  cursor: pointer;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: #3a3a3a;
  transition: background-color 0.3s cubic-bezier(0.4, 0.0, 0.2, 1);
  border-radius: 20px;
}
#${guiId} .toggle-slider:before {
  position: absolute;
  content: '';
  height: 16px;
  width: 16px;
  left: 2px;
  bottom: 2px;
  background-color: #e0e0e0;
  transition: transform 0.3s cubic-bezier(0.4, 0.0, 0.2, 1), background-color 0.3s ease;
  border-radius: 50%;
}
#${guiId} input:checked + .toggle-slider {
  background-color: #4CAF50;
}
#${guiId} input:checked + .toggle-slider:before {
  transform: translateX(20px);
}
#${guiId} .gui-slider-container {
  display: flex;
  flex-direction: column;
  margin-bottom: 12px;
  width: 85%;
}
#${guiId} .gui-slider-header {
  display: flex;
  justify-content: space-between;
  margin-bottom: 6px;
  font-size: 12px;
  color: #b0b0b0;
}
#${guiId} .gui-slider-value {
  font-weight: bold;
  color: #e0e0e0;
  transition: color 0.2s ease;
}
#${guiId} .gui-slider {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 6px;
  background: #3a3a3a;
  border-radius: 3px;
  outline: none;
  transition: background 0.2s ease, box-shadow 0.2s ease;
}
#${guiId} .gui-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #4CAF50;
  cursor: pointer;
  transition: background 0.2s ease, transform 0.15s ease, box-shadow 0.2s ease;
}
#${guiId} .gui-slider::-moz-range-thumb {
  width: 16px;
  height: 16px;
  border: none;
  border-radius: 50%;
  background: #4CAF50;
  cursor: pointer;
  transition: background 0.2s ease, transform 0.15s ease, box-shadow 0.2s ease;
}
#${guiId} .gui-slider:hover {
  background: #444444;
}
#${guiId} .gui-slider:hover::-webkit-slider-thumb {
  transform: scale(1.1);
  box-shadow: 0 0 8px rgba(76, 175, 80, 0.4);
}
#${guiId} .gui-slider:hover::-moz-range-thumb {
  transform: scale(1.1);
  box-shadow: 0 0 8px rgba(76, 175, 80, 0.4);
}
#${guiId} .gui-slider:focus {
  box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.2);
}
#${guiId} .gui-slider:focus::-webkit-slider-thumb {
  background: #5dbb61;
  box-shadow: 0 0 0 4px rgba(76, 175, 80, 0.3);
}
#${guiId} .gui-slider:focus::-moz-range-thumb {
  background: #5dbb61;
  box-shadow: 0 0 0 4px rgba(76, 175, 80, 0.3);
}
#${guiId} .gui-slider:active::-webkit-slider-thumb {
  transform: scale(1.2);
  background: #3d9140;
}
#${guiId} .gui-slider:active::-moz-range-thumb {
  transform: scale(1.2);
  background: #3d9140;
}
    `;
    document.head.appendChild(style);

    const gui = document.createElement('div');
    gui.id = guiId;
    let jss;
    const guiHeader = document.createElement('div');
    guiHeader.className = 'gui-header';

    const guiTitle = document.createElement('div');
    guiTitle.className = 'gui-title';
    guiTitle.textContent = `AnGUI - Sanuli V${version}`;

    const guiTabs = document.createElement('div');
    guiTabs.className = 'gui-tabs';

    const guiContent = document.createElement('div');
    guiContent.className = 'gui-content';

    guiHeader.appendChild(guiTitle);
    gui.appendChild(guiHeader);
    gui.appendChild(guiTabs);
    gui.appendChild(guiContent);
    document.body.appendChild(gui);

     tabs.forEach(tab => {
        const tabButton = document.createElement('div');
        tabButton.className = `gui-tab ${tab.active ? 'active' : ''}`;
        tabButton.textContent = tab.label;
        tabButton.dataset.tabId = tab.id;
        guiTabs.appendChild(tabButton);

        const tabContent = document.createElement('div');
        tabContent.className = `tab-content ${tab.active ? 'active' : ''}`;
        tabContent.id = `tab-${tab.id}`;
        guiContent.appendChild(tabContent);

        tabButton.addEventListener('click', () => {
            document.querySelectorAll(`#${guiId} .gui-tab`).forEach(t => {
                t.classList.remove('active');
            });
            document.querySelectorAll(`#${guiId} .tab-content`).forEach(c => {
                c.classList.remove('active');
            });

            tabButton.classList.add('active');
            tabContent.classList.add('active');
        });
    });

        let isVisible = true;
        let dragActive = false;
        let dragStartX, dragStartY, initialX, initialY;

        document.addEventListener('keydown', (e) => {
            if (e.key === 'Insert') {
                isVisible = !isVisible;
                gui.style.display = isVisible ? 'block' : 'none';
            }
        });

        guiHeader.addEventListener('mousedown', (e) => {
            startD(e.clientX, e.clientY);
            e.preventDefault();
        });

        document.addEventListener('mousemove', (e) => {
            if (dragActive) {
                moveD(e.clientX, e.clientY);
            }
        });

        document.addEventListener('mouseup', () => {
            endD();
        });

        guiHeader.addEventListener('touchstart', (e) => {
            if (e.touches.length === 1) {
                const touch = e.touches[0];
                startD(touch.clientX, touch.clientY);
                e.preventDefault();
            }
        });

        document.addEventListener('touchmove', (e) => {
            if (dragActive && e.touches.length === 1) {
                const touch = e.touches[0];
                moveD(touch.clientX, touch.clientY);
                e.preventDefault();
            }
        });

        document.addEventListener('touchend', () => {
            endD();
        });

        document.addEventListener('touchcancel', () => {
            endD();
        });

        function startD(x, y) {
            dragActive = true;
            dragStartX = x;
            dragStartY = y;
            initialX = gui.offsetLeft;
            initialY = gui.offsetTop;
            gui.style.cursor = 'grabbing';
        }

        function moveD(x, y) {
            const dx = x - dragStartX;
            const dy = y - dragStartY;
            gui.style.left = initialX + dx + 'px';
            gui.style.top = initialY + dy + 'px';
            gui.style.right = 'auto';
            if (!localStorage.getItem('gui')) {
            const jss = {
                'l': gui.style.left,
                't': gui.style.top,
                'r': gui.style.right,
                'o': 0.95,
            };
            localStorage.setItem('gui', JSON.stringify(jss));
            } else {
                const read = JSON.parse(localStorage.getItem('gui'));
                const jss = {
                'l': gui.style.left,
                't': gui.style.top,
                'r': gui.style.right,
                'o': read.o,
                };
                localStorage.setItem('gui', JSON.stringify(jss));
            }
        }

        function endD() {
            if (dragActive) {
                dragActive = false;
                gui.style.cursor = 'default';
            }
        }

        let inputFocused = false;

        document.addEventListener('keydown', function(event) {
        if (event.key === '=') {
            if (confirm('Oletko varma, että haluat nollaa käyttöjärjestelmän?')) {
                   localStorage.removeItem('gui');
                   alert('käyttöjärjestelmä on nollattu.');
                   location.reload();
                }
        }
    });

    document.addEventListener('keydown', function(event) {
        if (event.key === '!') {
            if (confirm('Oletko varma, että haluat nollaa sanulin tiedot?')) {
                   const keep = localStorage.getItem('gui');

                   for (let i = localStorage.length - 1; i >= 0; i--) {
                       const key = localStorage.key(i);
                       if (key !== 'gui') {
                           localStorage.removeItem(key);
                       }
                   }

                if (keep !== null) {
                    localStorage.setItem('gui', keep);
                }
                   alert('sanulin tiedot on nollattu.');
                   location.reload();
                }
        }
    });

   const createInput = (tabId, info, placeholder, buttonText, type, callback) => {
       const tabContent = document.getElementById(`tab-${tabId}`);
        if (!tabContent) return;

        const item = document.createElement('div');
        item.className = 'gui-item';

        const itemInfo = document.createElement('div');
        itemInfo.className = 'gui-item-info';
        itemInfo.textContent = info;

        const inputContainer = document.createElement('div');
        inputContainer.className = 'gui-input-container';

        const input = document.createElement('input');
        input.type = type;
        input.placeholder = placeholder;

        input.addEventListener('focus', () => {
        inputFocused = true;
    });

    input.addEventListener('blur', () => {
        inputFocused = false;
    });

    input.addEventListener('keydown', (event) => {
        event.stopPropagation();
    });

        const button = document.createElement('button');
        button.textContent = buttonText;
        button.addEventListener('click', () => {
            callback(input.value);
        });

        inputContainer.appendChild(input);
        inputContainer.appendChild(button);

        item.appendChild(itemInfo);
        item.appendChild(inputContainer);
        tabContent.appendChild(item);
    };

const createSlider = (tabId, info, value, step, min, max, callback) => {
    const tabContent = document.getElementById(`tab-${tabId}`);
    if (!tabContent) return;

    const item = document.createElement('div');
    item.className = 'gui-item';

    const itemInfo = document.createElement('div');
    itemInfo.className = 'gui-item-info';
    itemInfo.textContent = info;

    const sliderContainer = document.createElement('div');
    sliderContainer.className = 'gui-slider-container';

    const sliderHeader = document.createElement('div');
    sliderHeader.className = 'gui-slider-header';

    const sliderLabel = document.createElement('span');
    sliderLabel.textContent = info;

    const valueLabel = document.createElement('span');
    valueLabel.className = 'gui-slider-value';
    valueLabel.textContent = value;

    sliderHeader.appendChild(sliderLabel);
    sliderHeader.appendChild(valueLabel);

    const slider = document.createElement('input');
    slider.className = 'gui-slider';
    slider.type = 'range';
    slider.min = min;
    slider.max = max;
    slider.step = (max - min) / step;
    slider.value = value;

    slider.addEventListener('input', () => {
        valueLabel.textContent = slider.value;
        callback(slider.value);
    });

    sliderContainer.appendChild(sliderHeader);
    sliderContainer.appendChild(slider);

    item.appendChild(sliderContainer);
    tabContent.appendChild(item);

    return slider;
};


    const createToggle = (tabId, info, name, onFunc, offFunc) => {
        const tabContent = document.getElementById(`tab-${tabId}`);
        if (!tabContent) return;

        const item = document.createElement('div');
        item.className = 'gui-item';

        const itemInfo = document.createElement('div');
        itemInfo.className = 'gui-item-info';
        itemInfo.textContent = info;

        const toggleContainer = document.createElement('div');
        toggleContainer.className = 'toggle-container';

        const toggleLabel = document.createElement('label');
        toggleLabel.className = 'toggle-switch';

        const toggleInput = document.createElement('input');
        toggleInput.type = 'checkbox';
        toggleInput.name = name;

        const toggleSlider = document.createElement('span');
        toggleSlider.className = 'toggle-slider';

        toggleInput.addEventListener('change', () => {
            if (toggleInput.checked) {
                onFunc();
            } else {
                offFunc();
            }
        });

        toggleLabel.appendChild(toggleInput);
        toggleLabel.appendChild(toggleSlider);
        toggleContainer.appendChild(toggleLabel);

        item.appendChild(itemInfo);
        item.appendChild(toggleContainer);
        tabContent.appendChild(item);

        return toggleInput;
    };

    const createButton = (tabId, info, buttonText, callback) => {
        const tabContent = document.getElementById(`tab-${tabId}`);
        if (!tabContent) return;

        const item = document.createElement('div');
        item.className = 'gui-item';

        const itemInfo = document.createElement('div');
        itemInfo.className = 'gui-item-info';
        itemInfo.textContent = info;

        const button = document.createElement('button');
        button.textContent = buttonText;
        button.addEventListener('click', callback);

        item.appendChild(itemInfo);
        item.appendChild(button);
        tabContent.appendChild(item);

        return button;
    };

    inputs.forEach(input => {
        createInput(input.tab, input.info, input.placeholder, input.buttonText, input.type, input.callback);
    });

    toggles.forEach(toggle => {
        createToggle(toggle.tab, toggle.info, toggle.name, toggle.onFunc, toggle.offFunc);
    });

    buttons.forEach(button => {
        createButton(button.tab, button.info, button.buttonText, button.callback);
    });

    sliders.forEach(slider => {
        createSlider(slider.tab, slider.info, slider.value, slider.step, slider.min, slider.max, slider.callback);
    });

    if (localStorage.getItem('gui')) {
      const vals = JSON.parse(localStorage.getItem('gui'));
       gui.style.left = vals.l;
       gui.style.right = vals.r;
       gui.style.top = vals.t;
    }
    let item = null;
    if (isMobile()) {
       const elems = document.querySelectorAll('div.keyboard-row');
        const target = Array.from(elems).find(el => el.childElementCount === 13);
        item = document.createElement('button');
        item.className = 'keyboard-button keyboard-button-submit';
        item.style.backgroundColor = 'purple';
        item.textContent = 'angui';
        target.appendChild(item);
    }
    if (item) {
        item.addEventListener('touchstart', (e) => {
            if (e.touches.length === 1) {
                isVisible = !isVisible;
                gui.style.display = isVisible ? 'block' : 'none';
            }
        });
    }

    } else {
      alert('Tämä koodi toimii vain sivustolla sanuli.fi, avataan sivu.');
        document.location.href = 'https://sanuli.fi/';
    }
})();
    // tekijä: theyhoppingonme
}, 200);
}
const waitForDiv = () => {
    const div = document.querySelector('.keyboard');
    if (div) {
        main();
    } else {
        requestAnimationFrame(waitForDiv);
    }
};

waitForDiv();