Greasy Fork is available in English.

Advanced Scratch Block Customizer

Customize Scratch blocks, create custom blocks, and access advanced settings with a modern UI.

// ==UserScript==
// @name         Advanced Scratch Block Customizer
// @namespace    http://tampermonkey.net/
// @version      1.3
// @description  Customize Scratch blocks, create custom blocks, and access advanced settings with a modern UI.
// @match        *://scratch.mit.edu/*
// @grant        GM_addStyle
// ==/UserScript==

(function() {
    'use strict';

    // Create the main GUI container
    const guiContainer = document.createElement('div');
    guiContainer.id = 'scratchCustomizerGui';
    document.body.appendChild(guiContainer);

    // Style the GUI
    GM_addStyle(`
        #scratchCustomizerGui {
            position: fixed;
            top: 20px;
            right: 20px;
            width: 400px;
            height: 600px;
            background: #ffffff;
            border: 1px solid #ddd;
            border-radius: 8px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
            z-index: 10000;
            padding: 20px;
            display: none;
            overflow-y: auto;
            font-family: Arial, sans-serif;
            color: #333;
        }
        #scratchCustomizerGui h2 {
            margin-top: 0;
            font-size: 24px;
            text-align: center;
            color: #007bff;
        }
        #scratchCustomizerGui .gui-section {
            margin-bottom: 20px;
            border-bottom: 1px solid #eee;
            padding-bottom: 15px;
        }
        #scratchCustomizerGui .gui-section h3 {
            font-size: 20px;
            color: #333;
        }
        #scratchCustomizerGui button {
            display: block;
            width: 100%;
            margin: 10px 0;
            padding: 12px;
            font-size: 16px;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            background: #007bff;
            color: #fff;
            transition: background-color 0.3s;
        }
        #scratchCustomizerGui button:hover {
            background: #0056b3;
        }
        #scratchCustomizerGui button:active {
            background: #004494;
        }
        #scratchCustomizerGui .color-picker,
        #scratchCustomizerGui select,
        #scratchCustomizerGui input,
        #scratchCustomizerGui textarea {
            width: calc(100% - 22px);
            padding: 10px;
            margin: 10px 0;
            border-radius: 4px;
            border: 1px solid #ddd;
            box-sizing: border-box;
        }
        #scratchCustomizerGui .custom-block {
            display: flex;
            align-items: center;
            margin-bottom: 10px;
        }
        #scratchCustomizerGui .custom-block input {
            margin-right: 10px;
        }
        #openScratchCustomizerButton {
            position: fixed;
            bottom: 20px;
            right: 20px;
            padding: 10px 15px;
            background: #007bff;
            color: #fff;
            border-radius: 5px;
            border: none;
            cursor: pointer;
            z-index: 10001;
            font-size: 16px;
            transition: background-color 0.3s;
        }
        #openScratchCustomizerButton:hover {
            background: #0056b3;
        }
        #openScratchCustomizerButton:active {
            background: #004494;
        }
        #scratchCustomizerGui .theme-option {
            display: inline-block;
            margin: 0 10px;
            padding: 10px;
            border-radius: 5px;
            cursor: pointer;
            text-align: center;
        }
        #scratchCustomizerGui .theme-option:hover {
            background: #f0f0f0;
        }
    `);

    // Add an Open button to toggle the GUI
    const openButton = document.createElement('button');
    openButton.id = 'openScratchCustomizerButton';
    openButton.innerText = 'Open Customizer';
    document.body.appendChild(openButton);

    // Event to open/close the GUI
    openButton.addEventListener('click', () => {
        guiContainer.style.display = guiContainer.style.display === 'none' ? 'block' : 'none';
    });

    // Add close button to GUI
    const closeButton = document.createElement('button');
    closeButton.innerText = 'Close';
    closeButton.addEventListener('click', () => {
        guiContainer.style.display = 'none';
    });
    guiContainer.appendChild(closeButton);

    // GUI Title
    const guiTitle = document.createElement('h2');
    guiTitle.innerText = 'Advanced Scratch Customizer';
    guiContainer.appendChild(guiTitle);

    // Section: Block Selection and Modification
    const blockSelectionSection = document.createElement('div');
    blockSelectionSection.className = 'gui-section';
    blockSelectionSection.innerHTML = `
        <h3>Select and Modify Block</h3>
        <label>Select Block Category:</label>
        <select id="blockCategorySelector">
            <option value="motion">Motion</option>
            <option value="looks">Looks</option>
            <option value="sound">Sound</option>
            <option value="events">Events</option>
            <option value="control">Control</option>
            <option value="sensing">Sensing</option>
            <option value="operators">Operators</option>
            <option value="variables">Variables</option>
            <!-- Add more block categories if needed -->
        </select>
        <label>Select Specific Block:</label>
        <select id="specificBlockSelector">
            <!-- This will be dynamically populated based on category -->
        </select>
        <label>Pick a Block Color:</label>
        <input type="color" id="blockColorPicker" class="color-picker">
        <label>Pick a Text Color:</label>
        <input type="color" id="blockTextColorPicker" class="color-picker">
        <label>Pick a Block Background Color:</label>
        <input type="color" id="blockBgColorPicker" class="color-picker">
        <button id="applyBlockColorButton">Apply Changes</button>
    `;
    guiContainer.appendChild(blockSelectionSection);

    // Section: Custom Block Editor
    const customBlockSection = document.createElement('div');
    customBlockSection.className = 'gui-section';
    customBlockSection.innerHTML = `
        <h3>Custom Block Editor</h3>
        <button id="createCustomBlockButton">Create New Block</button>
        <button id="modifyBlockFunctionButton">Modify Block Functions</button>
        <div id="customBlockEditor" class="gui-section" style="display: none;">
            <h4>Custom Block Editor</h4>
            <label>Block Name:</label>
            <input type="text" id="customBlockName" placeholder="Enter block name">
            <label>Block Color:</label>
            <input type="color" id="customBlockColor" class="color-picker">
            <label>Block Function:</label>
            <textarea id="customBlockFunction" rows="4" placeholder="Enter block function code"></textarea>
            <button id="saveCustomBlockButton">Save Block</button>
        </div>
    `;
    guiContainer.appendChild(customBlockSection);

    // Section: Theme Chooser
    const themeSection = document.createElement('div');
    themeSection.className = 'gui-section';
    themeSection.innerHTML = `
        <h3>Theme Chooser</h3>
        <div id="themeOptions">
            <div class="theme-option" id="defaultTheme">Default</div>
            <div class="theme-option" id="darkTheme">Dark</div>
            <div class="theme-option" id="cupcakeTheme">Cupcake</div>
            <div class="theme-option" id="moonTheme">Moon</div>
            <div class="theme-option" id="sunTheme">Sun</div>
        </div>
    `;
    guiContainer.appendChild(themeSection);

    // Section: Custom Settings
    const settingsSection = document.createElement('div');
    settingsSection.className = 'gui-section';
    settingsSection.innerHTML = `
        <h3>Custom Settings</h3>
        <button id="openSettingsButton">Open Settings</button>
    `;
    guiContainer.appendChild(settingsSection);

    // Event Listener for Block Category Selection
    document.getElementById('blockCategorySelector').addEventListener('change', function() {
        const category = this.value;
        populateSpecificBlocks(category);
    });

    // Populate specific blocks based on the selected category
    function populateSpecificBlocks(category) {
        const blockSelector = document.getElementById('specificBlockSelector');
        blockSelector.innerHTML = ''; // Clear existing options

        // Define blocks based on categories
        const blocks = {
            motion: ['Move 10 Steps', 'Turn Right', 'Turn Left'],
            looks: ['Say Hello', 'Think Hmm', 'Switch Costume'],
            sound: ['Play Sound', 'Stop All Sounds'],
            events: ['When Flag Clicked', 'When Key Pressed'],
            control: ['Wait 1 Sec', 'Repeat 10 Times'],
            sensing: ['Ask and Wait', 'Touching Color'],
            operators: ['Pick Random 1 to 10', 'Join', 'Letter of'],
            variables: ['Set Variable', 'Change Variable']
        };

        blocks[category].forEach(block => {
            const option = document.createElement('option');
            option.value = block;
            option.text = block;
            blockSelector.appendChild(option);
        });
    }

    // Event Listener for Apply Block Color Button
    document.getElementById('applyBlockColorButton').addEventListener('click', () => {
        const blockType = document.getElementById('specificBlockSelector').value;
        const blockColor = document.getElementById('blockColorPicker').value;
        const textColor = document.getElementById('blockTextColorPicker').value;
        const bgColor = document.getElementById('blockBgColorPicker').value;

        applyBlockStyles(blockType, blockColor, textColor, bgColor);
    });

    // Event Listener for Create Custom Block Button
    document.getElementById('createCustomBlockButton').addEventListener('click', () => {
        document.getElementById('customBlockEditor').style.display = 'block';
    });

    // Event Listener for Modify Block Function Button
    document.getElementById('modifyBlockFunctionButton').addEventListener('click', () => {
        document.getElementById('customBlockEditor').style.display = 'block';
    });

    // Event Listener for Save Custom Block Button
    document.getElementById('saveCustomBlockButton').addEventListener('click', () => {
        const name = document.getElementById('customBlockName').value;
        const color = document.getElementById('customBlockColor').value;
        const functionCode = document.getElementById('customBlockFunction').value;

        // Save the custom block (to be implemented)
        alert(`Saved custom block: ${name}\nColor: ${color}\nFunction: ${functionCode}`);
    });

    // Event Listener for Apply Theme Button
    document.querySelectorAll('.theme-option').forEach(button => {
        button.addEventListener('click', () => {
            const theme = button.id.replace('Theme', '');
            applyTheme(theme);
        });
    });

    // Event Listener for Open Settings Button
    document.getElementById('openSettingsButton').addEventListener('click', () => {
        openCustomSettings();
    });

    // Function to apply block styles
    function applyBlockStyles(blockType, blockColor, textColor, bgColor) {
        alert(`Applying styles to block: ${blockType}\nColor: ${blockColor}\nText Color: ${textColor}\nBackground Color: ${bgColor}`);
        // Implement style application to blocks here
    }

    // Function to apply a theme
    function applyTheme(theme) {
        alert(`Applying theme: ${theme}`);
        // Implement theme application to the Scratch website here
    }

    // Function to open custom settings (To be implemented)
    function openCustomSettings() {
        alert('Custom Settings not yet implemented.');
    }
})();