Advertisement

← vibesdk

src/utils/screenshot.ts

5.7 KB Download file
import html2canvas from 'html2canvas-pro';

export interface ScreenshotOptions {
  quality?: number;
  format?: 'png' | 'jpeg' | 'webp';
  excludeElements?: string[]; // CSS selectors to exclude
  scale?: number;
  width?: number;
  height?: number;
  element?: HTMLElement; // Optional specific element to capture
}

export interface ScreenshotResult {
  dataUrl: string;
  blob: Blob;
  metadata: {
    timestamp: number;
    url: string;
    viewport: { width: number; height: number };
    userAgent: string;
  };
}

/**
 * Captures a screenshot of the current page with advanced options
 * Automatically handles hiding specified elements during capture
 */
export async function captureScreenshot(options: ScreenshotOptions = {}): Promise<ScreenshotResult> {
  const {
    quality = 0.8,
    format = 'jpeg',
    excludeElements = [],
    scale = 1,
    width,
    height,
    element
  } = options;

  // Store original styles of elements to hide
  const elementsToHide: Array<{ element: HTMLElement; originalStyle: string }> = [];
  
  try {
    // Hide specified elements
    excludeElements.forEach(selector => {
      const elements = document.querySelectorAll(selector);
      elements.forEach(el => {
        const htmlEl = el as HTMLElement;
        elementsToHide.push({
          element: htmlEl,
          originalStyle: htmlEl.style.display
        });
        htmlEl.style.display = 'none';
      });
    });

    // Capture screenshot of the specified element or the entire body
    const targetElement = element || document.body;
    const canvas = await html2canvas(targetElement, {
      useCORS: true,
      allowTaint: false,
      scale,
      width,
      height,
      backgroundColor: '#ffffff',
      logging: false,
      ignoreElements: (element) => {
        // Additional element filtering if needed
        return element.classList.contains('screenshot-exclude');
      }
    });

    // Convert to desired format
    const dataUrl = canvas.toDataURL(`image/${format}`, quality);
    
    // Convert to blob for easier handling
    const blob = await new Promise<Blob>((resolve) => {
      canvas.toBlob((blob) => {
        resolve(blob!);
      }, `image/${format}`, quality);
    });

    return {
      dataUrl,
      blob,
      metadata: {
        timestamp: Date.now(),
        url: window.location.href,
        viewport: {
          width: window.innerWidth,
          height: window.innerHeight
        },
        userAgent: navigator.userAgent
      }
    };
  } finally {
    // Restore original styles
    elementsToHide.forEach(({ element, originalStyle }) => {
      element.style.display = originalStyle;
    });
  }
}

/**
 * Captures a screenshot specifically for debug purposes
 * Automatically excludes debug panels and overlays
 */
export async function captureDebugScreenshot(): Promise<ScreenshotResult> {
  return captureScreenshot({
    quality: 0.8,
    format: 'jpeg',
    excludeElements: [
      '[data-debug-panel]',
      '[data-overlay]',
      '.debug-panel',
      '.modal-overlay',
      '.toast-container',
      '.notification'
    ],
    scale: 1
  });
}

/**
 * Captures a screenshot for deployment documentation
 * High quality capture for production use
 */
export async function captureDeploymentScreenshot(): Promise<ScreenshotResult> {
  return captureScreenshot({
    quality: 0.9,
    format: 'png',
    excludeElements: [
      '[data-debug-panel]',
      '[data-overlay]',
      '.debug-panel'
    ],
    scale: 1
  });
}

/**
 * Auto screenshot system for tracking page states
 */
export class AutoScreenshotSystem {
  private enabled = false;
  private lastScreenshot: ScreenshotResult | null = null;
  private screenshotHistory: ScreenshotResult[] = [];
  private maxHistory = 10;

  constructor(private onScreenshot?: (screenshot: ScreenshotResult) => void) {}

  enable() {
    this.enabled = true;
    this.setupListeners();
  }

  disable() {
    this.enabled = false;
  }

  private setupListeners() {
    // Capture on page navigation
    window.addEventListener('popstate', () => {
      if (this.enabled) {
        this.captureAuto('navigation');
      }
    });

    // Capture on significant DOM changes (debounced)
    let domChangeTimeout: NodeJS.Timeout;
    const observer = new MutationObserver(() => {
      if (this.enabled) {
        clearTimeout(domChangeTimeout);
        domChangeTimeout = setTimeout(() => {
          this.captureAuto('dom_change');
        }, 2000);
      }
    });

    observer.observe(document.body, {
      childList: true,
      subtree: true,
      attributes: false
    });
  }

  private async captureAuto(trigger: string) {
    try {
      const screenshot = await captureScreenshot({
        quality: 0.6,
        format: 'jpeg',
        scale: 0.5,
        excludeElements: [
          '[data-debug-panel]',
          '[data-overlay]'
        ]
      });

      this.lastScreenshot = screenshot;
      this.screenshotHistory.push(screenshot);
      
      // Keep only recent screenshots
      if (this.screenshotHistory.length > this.maxHistory) {
        this.screenshotHistory.shift();
      }

      this.onScreenshot?.(screenshot);
      
      console.log(`Auto screenshot captured: ${trigger}`, {
        timestamp: screenshot.metadata.timestamp,
        url: screenshot.metadata.url
      });
    } catch (error) {
      console.error('Auto screenshot failed:', error);
    }
  }

  getHistory(): ScreenshotResult[] {
    return [...this.screenshotHistory];
  }

  getLastScreenshot(): ScreenshotResult | null {
    return this.lastScreenshot;
  }
}

// Global auto screenshot system instance
export const autoScreenshotSystem = new AutoScreenshotSystem();