File size: 2,617 Bytes
94193b5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
/**
 * Deployment Thumbnail Capture Utility
 * Captures a screenshot of a published deployment via a hidden iframe.
 * Returns a base64 data URL -- caller is responsible for persisting.
 */

import { captureIframeScreenshot, waitForResources } from './screenshot';

export interface DeploymentCaptureOptions {
  captureWidth?: number;
  captureHeight?: number;
  outputWidth?: number;
  outputHeight?: number;
  quality?: number;
  timeout?: number;
}

const DEFAULTS: Required<DeploymentCaptureOptions> = {
  captureWidth: 1280,
  captureHeight: 720,
  outputWidth: 640,
  outputHeight: 360,
  quality: 0.8,
  timeout: 15000,
};

/**
 * Capture a screenshot of a published deployment URL.
 * @returns base64 data URL, or null on failure
 */
export async function captureDeploymentScreenshot(
  deploymentUrl: string,
  options: DeploymentCaptureOptions = {}
): Promise<string | null> {
  const opts = { ...DEFAULTS, ...options };

  return new Promise((resolve) => {
    const iframe = document.createElement('iframe');
    iframe.style.position = 'fixed';
    iframe.style.top = '-10000px';
    iframe.style.left = '-10000px';
    iframe.style.width = `${opts.captureWidth}px`;
    iframe.style.height = `${opts.captureHeight}px`;
    iframe.style.border = 'none';
    iframe.src = deploymentUrl;

    let timeoutId: number | null = null;
    let resolved = false;

    const cleanup = () => {
      if (timeoutId) clearTimeout(timeoutId);
      if (iframe.parentElement) document.body.removeChild(iframe);
    };

    const done = (result: string | null) => {
      if (resolved) return;
      resolved = true;
      cleanup();
      resolve(result);
    };

    timeoutId = window.setTimeout(() => done(null), opts.timeout);

    iframe.onload = async () => {
      try {
        if (timeoutId) { clearTimeout(timeoutId); timeoutId = null; }
        timeoutId = window.setTimeout(() => done(null), 12000);

        try {
          const doc = iframe.contentDocument || iframe.contentWindow?.document;
          if (doc) await waitForResources(doc, 2500, 8000);
          else await new Promise(r => setTimeout(r, 2500));
        } catch {
          await new Promise(r => setTimeout(r, 2500));
        }

        const screenshot = await captureIframeScreenshot(
          iframe,
          opts.captureWidth,
          opts.captureHeight,
          opts.outputWidth,
          opts.outputHeight,
          opts.quality,
          false
        );

        done(screenshot);
      } catch {
        done(null);
      }
    };

    iframe.onerror = () => done(null);
    document.body.appendChild(iframe);
  });
}