download lovable shit

try to take over the lovable!

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==UserScript==
// @name         download lovable shit
// @namespace    http://tampermonkey.net/
// @version      2025-06-16
// @description  try to take over the lovable!
// @author       test4ment
// @match        https://lovable.dev/projects/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=lovable.dev
// @grant        none
// @require      https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.0/jszip.min.js
// @license MIT
// ==/UserScript==

const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
var was_clicked = false;
var zip = new JSZip();

function download() {
    var tree = document.getElementsByClassName("overflow-x-auto p-2")[0];
    if(tree === undefined){
        var but = document.getElementsByClassName("inline-flex items-center justify-center gap-2 text-sm font-medium focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 pointer-events-auto h-7 rounded-lg bg-muted p-[2px] transition-colors duration-150 ease-in-out hover:bg-muted-hover")[0];
        but.click();
        setTimeout(download, 1000);
    } else {
        if(was_clicked){
            worker(tree.children);
        }
        else{
            clickall(tree.children);
            was_clicked = true;
            setTimeout(download, 100);
        }
    };
};

async function clickall(nodes, depth = 5){
    for (let index = 0; index < depth; index++) {
        for (const child of nodes) {
            if(isFile(child)){
                child.click();
            } else {
                workerClicker(child.children[1].children)
            }
        }
        await sleep(1);
    }
}

function workerClicker(nodes){
    for (const child of nodes){
        if(!isFile(child)){
            workerClicker(child.children[1].children);
        }
    }
};

async function worker(nodes, fullfilename = [zip], first_call = true){
    for (const child of nodes) {
        if(isFile(child)){
            child.click();
            await sleep(30);
            var file_name = child.children[0].children[2].textContent;

            var code_field = document.getElementsByClassName("cm-content")[0];
            var scroller = document.getElementsByClassName("cm-scroller")[0];

            var file_content = Array.from(code_field.children)
            .filter(e=>e.className !== "cm-gap")
            .map(e => e.outerText).filter(e => e !== "\n");

            const scrollval = 1500;

            if(scroller.scrollHeight > 1000){
                for(let i = 1; i <= scroller.scrollHeight / scrollval + 1; i++){
                    scroller.scrollTop = i*scrollval;
                    await sleep(200);
                    var lines = Array.from(code_field.children)
                    .filter(e=>
                            e.className !== "cm-activeLine cm-line"
                            && e.className !== "cm-gap")
                    .map(e => e.outerText)
                    .filter(e => e !== "\n");

                    // Find the maximum overlap between end of file_content and start of lines
                    let maxOverlap = 0;
                    const maxCheck = Math.min(file_content.length, lines.length);

                    for (let overlapSize = maxCheck; overlapSize > 0; overlapSize--) {
                        const fileSuffix = file_content.slice(-overlapSize);
                        const linePrefix = lines.slice(0, overlapSize);
                        if (fileSuffix.join("\n") === linePrefix.join("\n")) {
                            maxOverlap = overlapSize;
                            break; // Found the largest overlap
                        }
                    }

                    // Merge the lines without duplication
                    if (maxOverlap > 0 && maxOverlap !== maxCheck) {
                        file_content = file_content.concat(lines.slice(maxOverlap));
                    }

                    //for(let j = 0; j < lines.length; j++){
                    //    if(!(file_content.includes(lines[j]))){
                    //        file_content = file_content.concat(lines.slice(j));
                    //        break;
                    //    }
                    //}
                }
            }

            fullfilename[fullfilename.length - 1].file(file_name, file_content.join("\n"));
        }
        else{
            var folder_name = child.children[0].children[1].textContent;
            var folder = fullfilename[fullfilename.length - 1].folder(folder_name);
            await worker(child.children[1].children, fullfilename.slice().concat([folder]), false);
        }
    }
    if(first_call){
        await sleep(5);
        await zip.generateAsync({ type: "blob" })
            .then(function(content) {
            const url = URL.createObjectURL(content);
            const a = document.createElement("a");
            a.href = url;
            a.download = document.getElementsByClassName("hidden truncate text-sm font-medium md:block")[0].outerText;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            URL.revokeObjectURL(url);
        });
        await sleep(5);
        zip = new JSZip();
    }
};

function isFile(node) {
    if(node.children.length == 2) return false;
    node.children[0].click();
    if(node.children.length == 2) return false;
    else return true;
};

(function() {
    'use strict';

    window.onload = (event) => {
        // Create a new button element
        var btn = document.createElement('button');
        btn.innerHTML = 'download';
        btn.style.position = 'fixed'; // Position it fixed on the screen
        btn.style.top = '30px'; // Distance from top
        btn.style.right = '100px'; // Distance from right
        btn.style.zIndex = 9999; // Make sure it appears on top
        btn.style.padding = '10px 20px';
        btn.style.backgroundColor = 'white';
        btn.style.color = 'black';
        btn.style.border = 'none';
        btn.style.borderRadius = '5px';
        btn.style.cursor = 'pointer';

        btn.addEventListener('click', download);

        document.body.insertBefore(btn, document.body.firstChild)
    };
})();