B

Blink.new Files Downloader

Copyโ€“paste script to export your Blink.new project files as a ZIP

  1. Open your Blink.new project

    Make sure the project is loaded in your browser.

  2. Expand the file tree (open files/folders)

    Click through files so the editor loads them (the script can capture opened Monaco models).

  3. Open DevTools Console

    Press F12 (or Ctrl+Shift+I on Windows, Cmd+Option+I on macOS) and switch to the Console tab.

  4. Paste the script below and press Enter

    The script loads JSZip, captures files from Monaco, then downloads a ZIP.

/**
 * BLINK.NEW FIXED SCRAPER v3.0
 * 
 * This version is specifically designed for Blink.new's actual structure
 * Works with the file tree visible in your screenshot!
 * 
 * HOW TO USE:
 * 1. Keep Blink.new project open
 * 2. Press F12 (Console)
 * 3. Paste THIS script
 * 4. Press Enter
 * 5. Wait for download!
 */

(async function() {
  console.log('๐Ÿ”ฅ Blink.new FIXED Scraper v3.0 Starting...');
  console.log('โณ Please wait...');
  
  // Load JSZip
  if (typeof JSZip === 'undefined') {
    const script = document.createElement('script');
    script.src = 'https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js';
    document.head.appendChild(script);
    
    await new Promise(resolve => {
      script.onload = resolve;
      setTimeout(resolve, 3000);
    });
  }
  
  if (typeof JSZip === 'undefined') {
    alert('โŒ Failed to load JSZip. Check internet and try again.');
    return;
  }
  
  console.log('โœ… JSZip loaded');
  
  const fileContents = new Map();
  
  function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
  
  // ============================================
  // STEP 1: Get Monaco Content (Primary Method)
  // ============================================
  console.log('๐Ÿ“‚ Step 1: Checking Monaco Editor...');
  
  function getMonacoFiles() {
    const files = [];
    
    if (window.monaco && window.monaco.editor) {
      const models = window.monaco.editor.getModels();
      console.log(`โœ… Found ${models.length} Monaco models`);
      
      for (const model of models) {
        try {
          const uri = model.uri.toString();
          const content = model.getValue();
          
          // Extract path from URI
          // URIs look like: file:///src/App.tsx or inmemory://model/1
          let path = uri;
          
          // Clean up the path
          if (path.startsWith('file://')) {
            path = path.replace(/^file:\/\/+/, '');
          } else if (path.startsWith('inmemory://')) {
            path = path.replace(/^inmemory:\/\/[^/]+\//, '');
          }
          
          // Remove leading slashes
          path = path.replace(/^\/+/, '');
          
          // If path is just a number or looks weird, use the content to guess
          if (!path.includes('.') || path.match(/^[0-9]+$/)) {
            // Try to detect file type from content
            if (content.includes('import React') || content.includes('export default')) {
              path = `file-${files.length + 1}.tsx`;
            } else if (content.trim().startsWith('{')) {
              path = `file-${files.length + 1}.json`;
            } else if (content.includes('@apply') || content.includes('background:')) {
              path = `file-${files.length + 1}.css`;
            } else {
              path = `file-${files.length + 1}.txt`;
            }
          }
          
          if (content && content.trim().length > 0) {
            files.push({ path, content });
            fileContents.set(path, content);
          }
        } catch (e) {
          console.warn('Failed to process model:', e);
        }
      }
    }
    
    return files;
  }
  
  const initialFiles = getMonacoFiles();
  console.log(`๐Ÿ“„ Initial extraction: ${initialFiles.length} files`);
  
  for (const file of initialFiles) {
    console.log(`  โœ… ${file.path} (${file.content.length} chars)`);
  }
  
  // ============================================
  // STEP 2: Find and Click Files
  // ============================================
  console.log('๐Ÿ–ฑ๏ธ  Step 2: Looking for clickable files...');
  
  // Find all elements that might be file entries
  // Look for elements with file extensions in their text
  const allElements = document.querySelectorAll('*');
  const fileElements = [];
  
  for (const el of allElements) {
    const text = el.textContent;
    
    // Must be short enough to be a filename
    if (text && text.length < 100 && text.length > 3) {
      // Check if it matches a filename pattern
      const fileMatch = text.match(/^[\w-]+\.(tsx?|jsx?|css|s?css|html?|json|md|xml|ya?ml|ts|js)$/i);
      
      if (fileMatch) {
        const fileName = text.trim();
        
        // Check if element or parent is clickable
        const isClickable = 
          el.onclick ||
          el.tagName === 'BUTTON' ||
          el.role === 'button' ||
          el.parentElement?.onclick ||
          window.getComputedStyle(el).cursor === 'pointer';
        
        if (isClickable) {
          fileElements.push({ el, fileName });
        }
      }
    }
  }
  
  console.log(`โœ… Found ${fileElements.length} clickable files`);
  
  // Click through files
  for (let i = 0; i < fileElements.length; i++) {
    const { el, fileName } = fileElements[i];
    
    console.log(`  [${i + 1}/${fileElements.length}] Clicking: ${fileName}`);
    
    try {
      el.click();
      await sleep(600);
      
      // Get new Monaco content
      const newFiles = getMonacoFiles();
      const newCount = newFiles.length - initialFiles.length;
      
      if (newCount > 0) {
        console.log(`    โœ… +${newCount} new file(s) loaded`);
      }
    } catch (e) {
      console.warn(`    โš ๏ธ  Click failed:`, e.message);
    }
  }
  
  // ============================================
  // STEP 3: Final Monaco Extraction
  // ============================================
  console.log('๐Ÿ” Step 3: Final extraction...');
  
  const finalFiles = getMonacoFiles();
  console.log(`โœ… Total unique files: ${fileContents.size}`);
  
  // ============================================
  // STEP 4: Create ZIP
  // ============================================
  console.log('๐Ÿ“ฆ Step 4: Creating ZIP...');
  
  if (fileContents.size === 0) {
    console.error('โŒ No files were captured!');
    alert('โŒ No files captured!\n\nTry this:\n1. Click a few files manually in Blink\n2. Run this script again\n3. It will capture what you clicked');
    return;
  }
  
  const zip = new JSZip();
  
  // Add all files
  for (const [path, content] of fileContents.entries()) {
    zip.file(path, content);
  }
  
  // Add README
  const readme = `# Blink.new Project Export

Exported on: ${new Date().toLocaleString()}
Total files: ${fileContents.size}

## Files Included

${Array.from(fileContents.keys()).map(f => `- ${f}`).join('\n')}

## Notes

This export was created using the Fixed Scraper v3.0.
Files were extracted from Monaco Editor memory.

---
Created by Shihab Soft
`;
  
  zip.file('README-EXPORT.md', readme);
  
  // Generate ZIP
  console.log('โณ Generating ZIP...');
  const blob = await zip.generateAsync({ 
    type: 'blob',
    compression: 'DEFLATE',
    compressionOptions: { level: 9 }
  });
  
  // Download
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = `blink-project-${Date.now()}.zip`;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  URL.revokeObjectURL(url);
  
  // ============================================
  // SUCCESS!
  // ============================================
  console.log('');
  console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•');
  console.log('๐ŸŽ‰ SUCCESS!');
  console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•');
  console.log(`โœ… Files: ${fileContents.size}`);
  console.log(`โœ… Size: ${(blob.size / 1024).toFixed(2)} KB`);
  console.log('');
  console.log('๐Ÿ“‹ Downloaded files:');
  
  for (const [path, content] of fileContents.entries()) {
    console.log(`  โœ… ${path} (${(content.length / 1024).toFixed(2)} KB)`);
  }
  
  console.log('');
  console.log('๐Ÿ’พ Check your Downloads folder!');
  
  alert(`โœ… Success!\n\n${fileContents.size} files downloaded\n${(blob.size / 1024).toFixed(2)} KB\n\nCheck Downloads folder!`);
  
  // If only got a few files, suggest manual clicking
  if (fileContents.size < 5) {
    console.warn('');
    console.warn('๐Ÿ’ก TIP: Only got a few files?');
    console.warn('   1. Manually click files in Blink file tree');
    console.warn('   2. Run this script again');
    console.warn('   3. Each clicked file will be captured!');
  }
  
})();

Notes

  • If you only get a few files, manually click more files in the tree, then rerun the script.
  • Make sure you have a stable internet connection so JSZip can load from CDN.
  • This captures files present in Monaco editor memory (opened/loaded files).