download lovable shit

try to take over the lovable!

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==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)
    };
})();