Hoe ik 2k AI-prompts uit Higgsfield exporteerde

Vibe coding · 8 min leestijd
Hoe ik 2k AI-prompts uit Higgsfield exporteerde

Eerste echt klus voor mij met de desktop Claude code. En die werkt echt top! En props aan Claude om deze blog voor me te schrijven.

Na maanden intensief werken met Higgsfield had ik één groot praktisch probleem:
meer dan 2k gegenereerde beelden, elk met zorgvuldig opgebouwde prompts, die ik met mn getrainde Chatgpt - ZAPPA GPT heb gemaakt — en geen enkele manier om ze te exporteren.

Alles zat opgesloten in het platform.
En dat voelde niet oké.

Eerste echt klus voor mij met de desktop Claude code. En die werkt echt top! En props aan Claud om deze blog voor me te schrijven. Wel vond ik bijzonder dat de klus nog niet eens echt helemaal klaar was en ik al tegen de Claud PRO account limiet aanliep, als ik door wilde op naar MAX...... #zosnel?

Higgsfield prompts bewaren (nano banana pro)

Wie serieus met AI-beeldgeneratie werkt, weet:
je prompts zijn je kapitaal.

Je bouwt ze laag voor laag op. Je test. Je verfijnt. En op een gegeven moment heb je een bibliotheek die je niet kwijt wilt raken.

Maar wat als:

  • Higgsfield morgen stopt?
    Je je prompts wilt hergebruiken in een ander systeem?
    Je simpelweg een eigen backup wilt?

Een exportfunctie was er niet.
Een Chrome-extensie hielp alleen voor nieuwe prompts — niet voor mijn bestaande archief.

Poging 1 — De API

Mijn eerste gedachte: dit moet via een API kunnen.
Die bestaat ook, maar authenticatie zit volledig verweven in de frontend.

Resultaat: “Invalid or expired token”.
Geen toegang.

❌ Gefaald.

Poging 2 — Simpel browser-script

Daarna probeerde ik via de browser de Asset Library leeg te trekken.
Scrollen, images pakken, klaar.

Resultaat: 45 assets.
Van de 4500.

Bleek dat Higgsfield niet in de browser window scrollt, maar in een interne container.

❌ Weer mis.

Poging 3 — Juiste container, verkeerde aanname

Na wat speurwerk vond ik de echte scrollcontainer.
ScrollHeight: 272.533 pixels.

Maar de telling bleef raar fluctueren.
Soms meer assets, soms minder.

En dat was het moment waarop het kwartje viel.

Het echte issue — virtualized lists

Higgsfield laadt niet alle assets tegelijk.
Alleen wat je ziet bestaat in de DOM. De rest wordt dynamisch vervangen.

Mijn script telde aan het einde —
maar tegen die tijd waren de eerste duizenden items alweer verdwenen.

De oplossing — verzamelen tijdens het scrollen

In plaats van achteraf tellen, ben ik tijdens het scrollen gaan verzamelen.

Elke zichtbare asset-ID ging direct in een Set (geen duplicaten).
Daarna pas verder scrollen.

En toen ging het snel:

  • 300 assets
    2000 assets
    3000+ assets
    4571 unieke assets

✅ Gelukt.

Van assets naar echte prompts

Die 4571 assets bleken uiteindelijk ongeveer 2500 unieke prompts te zijn.
Logisch — vaak maak je meerdere variaties van één prompt.

Alles is automatisch gegroepeerd per prompt, met bijbehorende beelden.

Wat ik hiervan geleerd heb

  • Een API is niet altijd de oplossing
    Moderne webapps zijn slim — en soms bewust afgeschermd
    Debuggen doe je stap voor stap, niet in één sprong

  • Je creatieve werk moet van jou blijven

Hoe de export technisch is opgebouwd

Het proces bestaat uit drie stappen:

  1. Browser-script
    Scrollt door Higgsfield en verzamelt alle asset-IDs + prompts;
    Hierbij .js voor console.

    // higgsfield-prompt-exporter
    // **
     * ╔═══════════════════════════════════════════════════════════════╗
     * ║         HIGGSFIELD PROMPT EXPORTER                            ║
     * ║         Export naar Prompt Saver JSON format                  ║
     * ╚═══════════════════════════════════════════════════════════════╝
     *
     * GEBRUIK:
     * 1. Ga naar https://higgsfield.ai/asset/all
     * 2. Zorg dat je bent ingelogd
     * 3. Open browser console (F12 → Console tab)
     * 4. Kopieer dit HELE script en plak in de console
     * 5. Druk Enter
     * 6. Wacht tot het klaar is (kan 30-60 min duren bij 2000+ assets)
     * 7. JSON bestand wordt automatisch gedownload
     *
     * TIP: Laat de browser tab open en actief tijdens het scrapen
     */
    
    (async function HiggsFieldExporter() {
        'use strict';
    
        // ─────────────────────────────────────────────────────────────
        // CONFIGURATIE
        // ─────────────────────────────────────────────────────────────
        const CONFIG = {
            SCROLL_DELAY: 800,       // ms tussen scrolls (verhoogd!)
            SCROLL_STEP: 2000,       // pixels per scroll
            STABLE_ROUNDS: 10,       // meer rondes wachten voor "klaar"
            LOAD_DELAY: 1200,        // ms wachten na navigatie
            BATCH_LOG_SIZE: 50,      // log progress elke N assets
            MAX_ASSETS: 10000        // veiligheidsgrens
        };
    
        // ─────────────────────────────────────────────────────────────
        // STATE
        // ─────────────────────────────────────────────────────────────
        const promptsMap = new Map();  // Groepeer op unieke prompts
        let totalProcessed = 0;
        let errors = 0;
        const startTime = Date.now();
    
        console.clear();
        console.log('%c🚀 HIGGSFIELD PROMPT EXPORTER', 'font-size: 20px; font-weight: bold; color: #00ff00');
        console.log('━'.repeat(50));
    
        // ─────────────────────────────────────────────────────────────
        // STAP 1: SCROLL OM ALLE ASSETS TE LADEN
        // ─────────────────────────────────────────────────────────────
        console.log('\n📜 STAP 1: Alle assets laden (scrollen)...');
    
        // Ga eerst naar asset overzicht
        if (!window.location.pathname.includes('/asset/all')) {
            window.location.href = 'https://higgsfield.ai/asset/all';
            throw new Error('Navigeer naar /asset/all en run het script opnieuw');
        }
    
        console.log('   Zoeken naar scroll container...');
    
        // BELANGRIJK: Higgsfield gebruikt een interne scroll container, niet window!
        let scrollContainer = null;
        const allElements = document.querySelectorAll('div');
        for (const el of allElements) {
            const style = window.getComputedStyle(el);
            if ((style.overflowY === 'auto' || style.overflowY === 'scroll') &&
                el.scrollHeight > el.clientHeight + 100 &&
                el.scrollHeight > 5000) {
                scrollContainer = el;
                console.log(`   ✓ Scroll container gevonden (${el.scrollHeight}px)`);
                break;
            }
        }
    
        if (!scrollContainer) {
            console.error('   ❌ Geen scroll container gevonden! Probeer window...');
            scrollContainer = document.documentElement;
        }
    
        // VIRTUALIZED LIST FIX: Verzamel IDs TIJDENS scrollen!
        // Higgsfield verwijdert items uit DOM die niet zichtbaar zijn
        const collectedIds = new Set();
        let lastCollectedCount = 0;
        let noNewIdsRounds = 0;
    
        console.log('   Scroll door alle assets (verzamelt IDs onderweg)...');
    
        while (noNewIdsRounds < 15) {
            // Verzamel EERST alle zichtbare IDs voordat we scrollen
            const visibleImgs = document.querySelectorAll('img[alt*="media asset by id"]');
            visibleImgs.forEach(img => {
                const match = img.alt.match(/id of ([a-f0-9-]+)/);
                if (match) {
                    collectedIds.add(match[1]);
                }
            });
    
            // Check of we nieuwe IDs hebben gevonden
            if (collectedIds.size === lastCollectedCount) {
                noNewIdsRounds++;
            } else {
                noNewIdsRounds = 0;
                if (collectedIds.size % 100 < 10) {
                    console.log(`   📦 ${collectedIds.size} unieke assets verzameld...`);
                }
            }
            lastCollectedCount = collectedIds.size;
    
            // Scroll naar beneden
            scrollContainer.scrollTop += CONFIG.SCROLL_STEP;
            await sleep(CONFIG.SCROLL_DELAY);
    
            // Extra wacht als we onderaan zijn
            if (scrollContainer.scrollTop >= scrollContainer.scrollHeight - scrollContainer.clientHeight - 500) {
                await sleep(400);
                // Kleine scroll terug en weer vooruit om meer te laden
                scrollContainer.scrollTop -= 500;
                await sleep(200);
                scrollContainer.scrollTop += 600;
                await sleep(400);
            }
    
            if (collectedIds.size >= CONFIG.MAX_ASSETS) {
                console.log('   ⚠️ Maximum bereikt');
                break;
            }
        }
    
        // Scroll terug naar boven
        scrollContainer.scrollTop = 0;
        await sleep(500);
    
        // Convert Set naar Array
        const assetIds = Array.from(collectedIds);
    
        console.log(`✅ ${assetIds.length} unieke assets gevonden`);
    
        // ─────────────────────────────────────────────────────────────
        // STAP 2: PROMPTS OPHALEN VAN ELKE ASSET
        // ─────────────────────────────────────────────────────────────
        const estMinutes = Math.round(assetIds.length * 1.2 / 60);
        console.log(`\n🔍 STAP 2: Prompts ophalen (~${estMinutes} minuten)...`);
    
        for (let i = 0; i < assetIds.length; i++) {
            const assetId = assetIds[i];
    
            try {
                // Navigeer naar asset
                window.history.pushState({}, '', `/asset/all/${assetId}`);
                window.dispatchEvent(new PopStateEvent('popstate'));
                await sleep(CONFIG.LOAD_DELAY);
    
                // Extract prompt
                const data = extractPrompt();
    
                if (data.prompt && data.prompt.length > 10) {
                    const key = data.prompt.trim();
    
                    if (!promptsMap.has(key)) {
                        promptsMap.set(key, {
                            promptText: data.prompt,
                            model: data.model,
                            assetIds: []
                        });
                    }
    
                    promptsMap.get(key).assetIds.push(assetId);
                    totalProcessed++;
                }
    
            } catch (e) {
                errors++;
            }
    
            // Progress log
            if ((i + 1) % CONFIG.BATCH_LOG_SIZE === 0) {
                const pct = Math.round((i + 1) / assetIds.length * 100);
                const elapsed = (Date.now() - startTime) / 60000;
                const remaining = (elapsed / (i + 1)) * (assetIds.length - i - 1);
                console.log(`   ${pct}% (${i + 1}/${assetIds.length}) - nog ~${Math.round(remaining)} min`);
            }
        }
    
        // Terug naar overzicht
        window.history.pushState({}, '', '/asset/all');
        window.dispatchEvent(new PopStateEvent('popstate'));
    
        console.log(`✅ ${totalProcessed} prompts verzameld (${errors} errors)`);
    
        // ─────────────────────────────────────────────────────────────
        // STAP 3: EXPORTEER NAAR PROMPT SAVER FORMAT
        // ─────────────────────────────────────────────────────────────
        console.log('\n💾 STAP 3: JSON genereren...');
    
        const exportData = [];
    
        promptsMap.forEach((data, promptKey) => {
            // Maak titel van eerste 50 chars
            const title = data.promptText.replace(/[\n\r]+/g, ' ').trim();
            const shortTitle = title.length > 50 ? title.substring(0, 47) + '...' : title;
    
            exportData.push({
                title: shortTitle,
                promptText: data.promptText,
                imageUrl: `https://higgsfield.ai/asset/all/${data.assetIds[0]}`,
                // Extra metadata (Prompt Saver negeert dit)
                _meta: {
                    model: data.model,
                    totalImages: data.assetIds.length,
                    allUrls: data.assetIds.map(id => `https://higgsfield.ai/asset/all/${id}`)
                }
            });
        });
    
        // Download
        const filename = `higgsfield-prompts-${new Date().toISOString().slice(0, 10)}.json`;
        downloadJSON(exportData, filename);
    
        // ─────────────────────────────────────────────────────────────
        // KLAAR!
        // ─────────────────────────────────────────────────────────────
        const totalTime = Math.round((Date.now() - startTime) / 60000);
    
        console.log('\n' + '═'.repeat(50));
        console.log('%c✅ EXPORT VOLTOOID!', 'font-size: 16px; font-weight: bold; color: #00ff00');
        console.log('═'.repeat(50));
        console.log(`📊 ${exportData.length} unieke prompts`);
        console.log(`🖼️  ${totalProcessed} totale afbeeldingen`);
        console.log(`⏱️  ${totalTime} minuten`);
        console.log(`📁 Bestand: ${filename}`);
        console.log('═'.repeat(50));
    
        // Return voor debugging
        return exportData;
    
        // ─────────────────────────────────────────────────────────────
        // HELPER FUNCTIES
        // ─────────────────────────────────────────────────────────────
    
        function sleep(ms) {
            return new Promise(r => setTimeout(r, ms));
        }
    
        function extractPrompt() {
            const body = document.body.innerText;
    
            // Zoek prompt tussen "Copy" en "See all"
            const startIdx = body.indexOf('Copy');
            const endIdx = body.indexOf('See all');
    
            let prompt = '';
            if (startIdx > 0 && endIdx > startIdx) {
                prompt = body.substring(startIdx + 4, endIdx).trim();
            }
    
            // Zoek model naam
            let model = 'Unknown';
            const models = ['Nano Banana Pro', 'Kling', 'Minimax', 'Luma', 'Runway'];
            for (const m of models) {
                if (body.includes(m)) {
                    model = m;
                    break;
                }
            }
    
            return { prompt, model };
        }
    
        function downloadJSON(data, filename) {
            const json = JSON.stringify(data, null, 2);
            const blob = new Blob([json], { type: 'application/json' });
            const url = URL.createObjectURL(blob);
    
            const a = document.createElement('a');
            a.href = url;
            a.download = filename;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
    
            URL.revokeObjectURL(url);
        }
    
    })();
    
  2. Node.js-script
    Downloadt thumbnails en zet alles over naar eigen opslag

  3. Import
    Finale JSON import in mijn eigen prompt-bibliotheek

Totale tijd: ±2 uur
(waarvan het grootste deel gewoon wachten)