@teamflojo/floimg-screenshot 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +316 -0
- package/dist/index.d.ts +87 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +158 -0
- package/dist/index.js.map +1 -0
- package/package.json +57 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Brett Cooke
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
# imgflo-screenshot
|
|
2
|
+
|
|
3
|
+
Screenshot generator for imgflo using Playwright headless browser.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install imgflo imgflo-screenshot
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
This will automatically install Playwright and download Chromium (~200MB).
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import createClient from 'imgflo';
|
|
17
|
+
import screenshot from 'imgflo-screenshot';
|
|
18
|
+
|
|
19
|
+
const imgflo = createClient({
|
|
20
|
+
store: {
|
|
21
|
+
default: 's3',
|
|
22
|
+
s3: { region: 'us-east-1', bucket: 'my-screenshots' }
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Register the screenshot generator
|
|
27
|
+
imgflo.registerGenerator(screenshot());
|
|
28
|
+
|
|
29
|
+
// Screenshot a website
|
|
30
|
+
const site = await imgflo.generate({
|
|
31
|
+
generator: 'screenshot',
|
|
32
|
+
params: {
|
|
33
|
+
url: 'https://example.com',
|
|
34
|
+
width: 1920,
|
|
35
|
+
height: 1080
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Upload to S3
|
|
40
|
+
const result = await imgflo.save(site, './output/example.png');
|
|
41
|
+
console.log(result.url);
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Use Cases
|
|
45
|
+
|
|
46
|
+
### 1. Website Screenshots
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
await imgflo.generate({
|
|
50
|
+
generator: 'screenshot',
|
|
51
|
+
params: {
|
|
52
|
+
url: 'https://github.com/bcooke/imgflo',
|
|
53
|
+
width: 1280,
|
|
54
|
+
height: 800,
|
|
55
|
+
fullPage: true // Capture entire page
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### 2. HTML Rendering
|
|
61
|
+
|
|
62
|
+
Perfect for generating images from HTML/CSS:
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
await imgflo.generate({
|
|
66
|
+
generator: 'screenshot',
|
|
67
|
+
params: {
|
|
68
|
+
html: `
|
|
69
|
+
<div style="padding: 40px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white;">
|
|
70
|
+
<h1>Sales Report Q4</h1>
|
|
71
|
+
<p>Revenue: $1.2M</p>
|
|
72
|
+
</div>
|
|
73
|
+
`,
|
|
74
|
+
width: 800,
|
|
75
|
+
height: 600
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 3. Element Screenshots
|
|
81
|
+
|
|
82
|
+
Capture specific elements:
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
await imgflo.generate({
|
|
86
|
+
generator: 'screenshot',
|
|
87
|
+
params: {
|
|
88
|
+
url: 'https://example.com',
|
|
89
|
+
selector: '#main-content', // CSS selector
|
|
90
|
+
width: 1920,
|
|
91
|
+
height: 1080
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### 4. Wait for Content
|
|
97
|
+
|
|
98
|
+
Wait for dynamic content to load:
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
await imgflo.generate({
|
|
102
|
+
generator: 'screenshot',
|
|
103
|
+
params: {
|
|
104
|
+
url: 'https://example.com/dashboard',
|
|
105
|
+
waitFor: '.chart-loaded', // Wait for this selector
|
|
106
|
+
delay: 1000 // Additional 1s delay
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### 5. High DPI / Retina Screenshots
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
await imgflo.generate({
|
|
115
|
+
generator: 'screenshot',
|
|
116
|
+
params: {
|
|
117
|
+
url: 'https://example.com',
|
|
118
|
+
width: 1280,
|
|
119
|
+
height: 720,
|
|
120
|
+
deviceScaleFactor: 2 // 2x resolution
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Configuration
|
|
126
|
+
|
|
127
|
+
### Generator Options
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
imgflo.registerGenerator(screenshot({
|
|
131
|
+
persistent: true, // Reuse browser instance (faster)
|
|
132
|
+
defaultWidth: 1920,
|
|
133
|
+
defaultHeight: 1080,
|
|
134
|
+
launchOptions: {
|
|
135
|
+
headless: true,
|
|
136
|
+
args: ['--no-sandbox'] // Browser launch args
|
|
137
|
+
}
|
|
138
|
+
}));
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Persistent Browser Mode
|
|
142
|
+
|
|
143
|
+
For better performance when taking multiple screenshots:
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
const screenshotGen = screenshot({ persistent: true });
|
|
147
|
+
imgflo.registerGenerator(screenshotGen);
|
|
148
|
+
|
|
149
|
+
// Take multiple screenshots - browser stays open
|
|
150
|
+
await imgflo.generate({ generator: 'screenshot', params: { url: 'https://site1.com' } });
|
|
151
|
+
await imgflo.generate({ generator: 'screenshot', params: { url: 'https://site2.com' } });
|
|
152
|
+
await imgflo.generate({ generator: 'screenshot', params: { url: 'https://site3.com' } });
|
|
153
|
+
|
|
154
|
+
// Browser closes when process exits
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Parameters
|
|
158
|
+
|
|
159
|
+
| Parameter | Type | Default | Description |
|
|
160
|
+
|-----------|------|---------|-------------|
|
|
161
|
+
| `url` | string | - | URL to screenshot (required if no html) |
|
|
162
|
+
| `html` | string | - | HTML to render (required if no url) |
|
|
163
|
+
| `selector` | string | - | CSS selector to screenshot |
|
|
164
|
+
| `width` | number | 1280 | Viewport width |
|
|
165
|
+
| `height` | number | 720 | Viewport height |
|
|
166
|
+
| `fullPage` | boolean | false | Capture full scrollable page |
|
|
167
|
+
| `waitFor` | string | - | Selector to wait for |
|
|
168
|
+
| `delay` | number | - | Additional delay in ms |
|
|
169
|
+
| `deviceScaleFactor` | number | 1 | Device scale factor (for retina) |
|
|
170
|
+
| `format` | 'png' \| 'jpeg' | 'png' | Output format |
|
|
171
|
+
| `quality` | number | 90 | JPEG quality (0-100) |
|
|
172
|
+
|
|
173
|
+
## Examples
|
|
174
|
+
|
|
175
|
+
### OpenGraph Images
|
|
176
|
+
|
|
177
|
+
Generate OG images from HTML:
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
const ogImage = await imgflo.generate({
|
|
181
|
+
generator: 'screenshot',
|
|
182
|
+
params: {
|
|
183
|
+
html: `
|
|
184
|
+
<html>
|
|
185
|
+
<head>
|
|
186
|
+
<style>
|
|
187
|
+
body {
|
|
188
|
+
margin: 0;
|
|
189
|
+
width: 1200px;
|
|
190
|
+
height: 630px;
|
|
191
|
+
background: linear-gradient(135deg, #667eea, #764ba2);
|
|
192
|
+
color: white;
|
|
193
|
+
font-family: Arial, sans-serif;
|
|
194
|
+
display: flex;
|
|
195
|
+
align-items: center;
|
|
196
|
+
justify-content: center;
|
|
197
|
+
text-align: center;
|
|
198
|
+
}
|
|
199
|
+
h1 { font-size: 72px; margin: 0; }
|
|
200
|
+
p { font-size: 32px; opacity: 0.9; }
|
|
201
|
+
</style>
|
|
202
|
+
</head>
|
|
203
|
+
<body>
|
|
204
|
+
<div>
|
|
205
|
+
<h1>My Blog Post Title</h1>
|
|
206
|
+
<p>A great article about something interesting</p>
|
|
207
|
+
</div>
|
|
208
|
+
</body>
|
|
209
|
+
</html>
|
|
210
|
+
`,
|
|
211
|
+
width: 1200,
|
|
212
|
+
height: 630
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Dashboard Screenshot
|
|
218
|
+
|
|
219
|
+
Capture a live dashboard:
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
const dashboard = await imgflo.generate({
|
|
223
|
+
generator: 'screenshot',
|
|
224
|
+
params: {
|
|
225
|
+
url: 'https://internal-dashboard.company.com',
|
|
226
|
+
fullPage: true,
|
|
227
|
+
waitFor: '.dashboard-loaded',
|
|
228
|
+
width: 1920,
|
|
229
|
+
height: 1080
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Mobile Viewport
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
const mobile = await imgflo.generate({
|
|
238
|
+
generator: 'screenshot',
|
|
239
|
+
params: {
|
|
240
|
+
url: 'https://example.com',
|
|
241
|
+
width: 375, // iPhone size
|
|
242
|
+
height: 667,
|
|
243
|
+
deviceScaleFactor: 2
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## Performance Considerations
|
|
249
|
+
|
|
250
|
+
- **Cold start**: ~1-2 seconds for browser launch
|
|
251
|
+
- **Persistent mode**: Reuses browser, faster for multiple screenshots
|
|
252
|
+
- **Memory**: Each browser instance uses ~50-100MB RAM
|
|
253
|
+
- **Disk space**: Chromium download is ~200MB
|
|
254
|
+
|
|
255
|
+
## Playwright Documentation
|
|
256
|
+
|
|
257
|
+
For advanced browser control, see:
|
|
258
|
+
- [Playwright API](https://playwright.dev/docs/api/class-playwright)
|
|
259
|
+
- [Browser contexts](https://playwright.dev/docs/browser-contexts)
|
|
260
|
+
- [Selectors](https://playwright.dev/docs/selectors)
|
|
261
|
+
|
|
262
|
+
## Troubleshooting
|
|
263
|
+
|
|
264
|
+
### Browser fails to launch
|
|
265
|
+
|
|
266
|
+
If you see errors about missing dependencies:
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
# Install system dependencies
|
|
270
|
+
npx playwright install-deps chromium
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Screenshots timeout
|
|
274
|
+
|
|
275
|
+
Increase timeout for slow-loading pages:
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
await imgflo.generate({
|
|
279
|
+
generator: 'screenshot',
|
|
280
|
+
params: {
|
|
281
|
+
url: 'https://slow-site.com',
|
|
282
|
+
delay: 5000 // Wait 5 seconds
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Element not found
|
|
288
|
+
|
|
289
|
+
Ensure selector exists:
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
await imgflo.generate({
|
|
293
|
+
generator: 'screenshot',
|
|
294
|
+
params: {
|
|
295
|
+
url: 'https://example.com',
|
|
296
|
+
waitFor: '#my-element', // Wait for it to appear
|
|
297
|
+
selector: '#my-element' // Then screenshot it
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## Security Notes
|
|
303
|
+
|
|
304
|
+
- Never screenshot untrusted HTML (XSS risk)
|
|
305
|
+
- Be careful with URLs from user input
|
|
306
|
+
- Consider sandboxing in production
|
|
307
|
+
- Use `--no-sandbox` carefully (reduces security)
|
|
308
|
+
|
|
309
|
+
## License
|
|
310
|
+
|
|
311
|
+
MIT
|
|
312
|
+
|
|
313
|
+
## See Also
|
|
314
|
+
|
|
315
|
+
- [imgflo](https://github.com/bcooke/imgflo) - Core library
|
|
316
|
+
- [Playwright](https://playwright.dev) - Browser automation
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { ImageGenerator, GeneratorSchema } from "@teamflojo/floimg";
|
|
2
|
+
/**
|
|
3
|
+
* Schema for the Screenshot generator
|
|
4
|
+
*/
|
|
5
|
+
export declare const screenshotSchema: GeneratorSchema;
|
|
6
|
+
/**
|
|
7
|
+
* Screenshot generator using Playwright headless browser
|
|
8
|
+
*
|
|
9
|
+
* Supports:
|
|
10
|
+
* - Website screenshots (by URL)
|
|
11
|
+
* - HTML rendering (raw HTML)
|
|
12
|
+
* - Element screenshots (by selector)
|
|
13
|
+
* - Custom viewport sizes
|
|
14
|
+
* - Full page or viewport-only
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* import createClient from '@teamflojo/floimg';
|
|
19
|
+
* import screenshot from '@teamflojo/floimg-screenshot';
|
|
20
|
+
*
|
|
21
|
+
* const floimg = createClient();
|
|
22
|
+
* floimg.registerGenerator(screenshot());
|
|
23
|
+
*
|
|
24
|
+
* // Screenshot a website
|
|
25
|
+
* const site = await floimg.generate({
|
|
26
|
+
* generator: 'screenshot',
|
|
27
|
+
* params: {
|
|
28
|
+
* url: 'https://example.com',
|
|
29
|
+
* width: 1920,
|
|
30
|
+
* height: 1080
|
|
31
|
+
* }
|
|
32
|
+
* });
|
|
33
|
+
*
|
|
34
|
+
* // Render HTML
|
|
35
|
+
* const html = await floimg.generate({
|
|
36
|
+
* generator: 'screenshot',
|
|
37
|
+
* params: {
|
|
38
|
+
* html: '<h1>Hello World</h1>',
|
|
39
|
+
* width: 800,
|
|
40
|
+
* height: 600
|
|
41
|
+
* }
|
|
42
|
+
* });
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export interface ScreenshotConfig {
|
|
46
|
+
/** Reuse browser instance across screenshots (default: false) */
|
|
47
|
+
persistent?: boolean;
|
|
48
|
+
/** Browser launch options */
|
|
49
|
+
launchOptions?: {
|
|
50
|
+
headless?: boolean;
|
|
51
|
+
args?: string[];
|
|
52
|
+
};
|
|
53
|
+
/** Default viewport width (default: 1280) */
|
|
54
|
+
defaultWidth?: number;
|
|
55
|
+
/** Default viewport height (default: 720) */
|
|
56
|
+
defaultHeight?: number;
|
|
57
|
+
}
|
|
58
|
+
export interface ScreenshotParams extends Record<string, unknown> {
|
|
59
|
+
/** URL to screenshot */
|
|
60
|
+
url?: string;
|
|
61
|
+
/** Raw HTML to render */
|
|
62
|
+
html?: string;
|
|
63
|
+
/** CSS selector to screenshot (instead of full page) */
|
|
64
|
+
selector?: string;
|
|
65
|
+
/** Viewport width */
|
|
66
|
+
width?: number;
|
|
67
|
+
/** Viewport height */
|
|
68
|
+
height?: number;
|
|
69
|
+
/** Take full page screenshot (default: false) */
|
|
70
|
+
fullPage?: boolean;
|
|
71
|
+
/** Wait for selector before taking screenshot */
|
|
72
|
+
waitFor?: string;
|
|
73
|
+
/** Wait time in milliseconds before screenshot */
|
|
74
|
+
delay?: number;
|
|
75
|
+
/** Device scale factor (default: 1) */
|
|
76
|
+
deviceScaleFactor?: number;
|
|
77
|
+
/** Output format: 'png' | 'jpeg' (default: 'png') */
|
|
78
|
+
format?: 'png' | 'jpeg';
|
|
79
|
+
/** JPEG quality 0-100 (only for jpeg format) */
|
|
80
|
+
quality?: number;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Create a screenshot generator instance
|
|
84
|
+
*/
|
|
85
|
+
export default function screenshot(config?: ScreenshotConfig): ImageGenerator;
|
|
86
|
+
export { screenshot };
|
|
87
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAa,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpF;;GAEG;AACH,eAAO,MAAM,gBAAgB,EAAE,eAkD9B,CAAC;AAGF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,MAAM,WAAW,gBAAgB;IAC/B,iEAAiE;IACjE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,6BAA6B;IAC7B,aAAa,CAAC,EAAE;QACd,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;KACjB,CAAC;IACF,6CAA6C;IAC7C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6CAA6C;IAC7C,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,gBAAiB,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC/D,wBAAwB;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,yBAAyB;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qBAAqB;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sBAAsB;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,iDAAiD;IACjD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kDAAkD;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,qDAAqD;IACrD,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACxB,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,MAAM,GAAE,gBAAqB,GAAG,cAAc,CAkIhF;AAGD,OAAO,EAAE,UAAU,EAAE,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema for the Screenshot generator
|
|
3
|
+
*/
|
|
4
|
+
export const screenshotSchema = {
|
|
5
|
+
name: "screenshot",
|
|
6
|
+
description: "Capture screenshots of websites or render HTML using Playwright headless browser",
|
|
7
|
+
category: "Utility",
|
|
8
|
+
parameters: {
|
|
9
|
+
url: {
|
|
10
|
+
type: "string",
|
|
11
|
+
title: "URL",
|
|
12
|
+
description: "URL of the website to screenshot",
|
|
13
|
+
},
|
|
14
|
+
html: {
|
|
15
|
+
type: "string",
|
|
16
|
+
title: "HTML",
|
|
17
|
+
description: "Raw HTML content to render and screenshot",
|
|
18
|
+
},
|
|
19
|
+
selector: {
|
|
20
|
+
type: "string",
|
|
21
|
+
title: "Selector",
|
|
22
|
+
description: "CSS selector to screenshot a specific element instead of full page",
|
|
23
|
+
},
|
|
24
|
+
width: {
|
|
25
|
+
type: "number",
|
|
26
|
+
title: "Width",
|
|
27
|
+
description: "Viewport width in pixels",
|
|
28
|
+
default: 1280,
|
|
29
|
+
minimum: 100,
|
|
30
|
+
maximum: 4096,
|
|
31
|
+
},
|
|
32
|
+
height: {
|
|
33
|
+
type: "number",
|
|
34
|
+
title: "Height",
|
|
35
|
+
description: "Viewport height in pixels",
|
|
36
|
+
default: 720,
|
|
37
|
+
minimum: 100,
|
|
38
|
+
maximum: 4096,
|
|
39
|
+
},
|
|
40
|
+
fullPage: {
|
|
41
|
+
type: "boolean",
|
|
42
|
+
title: "Full Page",
|
|
43
|
+
description: "Take full page screenshot (default: false)",
|
|
44
|
+
default: false,
|
|
45
|
+
},
|
|
46
|
+
format: {
|
|
47
|
+
type: "string",
|
|
48
|
+
title: "Format",
|
|
49
|
+
description: "Output image format",
|
|
50
|
+
enum: ["png", "jpeg"],
|
|
51
|
+
default: "png",
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
import { chromium } from "playwright";
|
|
56
|
+
/**
|
|
57
|
+
* Create a screenshot generator instance
|
|
58
|
+
*/
|
|
59
|
+
export default function screenshot(config = {}) {
|
|
60
|
+
const { persistent = false, launchOptions = { headless: true }, defaultWidth = 1280, defaultHeight = 720, } = config;
|
|
61
|
+
let browser = null;
|
|
62
|
+
let cachedContext = null;
|
|
63
|
+
const getBrowser = async () => {
|
|
64
|
+
if (persistent && browser) {
|
|
65
|
+
return browser;
|
|
66
|
+
}
|
|
67
|
+
browser = await chromium.launch(launchOptions);
|
|
68
|
+
return browser;
|
|
69
|
+
};
|
|
70
|
+
const getContext = async (width, height, deviceScaleFactor) => {
|
|
71
|
+
// For persistent mode, reuse context if viewport size matches
|
|
72
|
+
// Otherwise, create new context each time
|
|
73
|
+
if (!persistent || !cachedContext) {
|
|
74
|
+
const browserInstance = await getBrowser();
|
|
75
|
+
cachedContext = await browserInstance.newContext({
|
|
76
|
+
viewport: { width, height },
|
|
77
|
+
deviceScaleFactor,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return cachedContext;
|
|
81
|
+
};
|
|
82
|
+
return {
|
|
83
|
+
name: "screenshot",
|
|
84
|
+
schema: screenshotSchema,
|
|
85
|
+
async generate(params) {
|
|
86
|
+
const { url, html, selector, width = defaultWidth, height = defaultHeight, fullPage = false, waitFor, delay, deviceScaleFactor = 1, format = 'png', quality = 90, } = params;
|
|
87
|
+
if (!url && !html) {
|
|
88
|
+
throw new Error("Either 'url' or 'html' parameter is required");
|
|
89
|
+
}
|
|
90
|
+
const context = await getContext(width, height, deviceScaleFactor);
|
|
91
|
+
const page = await context.newPage();
|
|
92
|
+
try {
|
|
93
|
+
// Load URL or HTML
|
|
94
|
+
if (url) {
|
|
95
|
+
await page.goto(url, { waitUntil: "networkidle" });
|
|
96
|
+
}
|
|
97
|
+
else if (html) {
|
|
98
|
+
await page.setContent(html, { waitUntil: "networkidle" });
|
|
99
|
+
}
|
|
100
|
+
// Wait for selector if specified
|
|
101
|
+
if (waitFor) {
|
|
102
|
+
await page.waitForSelector(waitFor, { timeout: 30000 });
|
|
103
|
+
}
|
|
104
|
+
// Additional delay if specified
|
|
105
|
+
if (delay) {
|
|
106
|
+
await page.waitForTimeout(delay);
|
|
107
|
+
}
|
|
108
|
+
// Take screenshot
|
|
109
|
+
let screenshotBytes;
|
|
110
|
+
if (selector) {
|
|
111
|
+
// Screenshot specific element
|
|
112
|
+
const element = await page.locator(selector).first();
|
|
113
|
+
const bytes = await element.screenshot({
|
|
114
|
+
type: format,
|
|
115
|
+
...(format === 'jpeg' && { quality }),
|
|
116
|
+
});
|
|
117
|
+
screenshotBytes = Buffer.from(bytes);
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
// Screenshot page
|
|
121
|
+
const bytes = await page.screenshot({
|
|
122
|
+
type: format,
|
|
123
|
+
fullPage,
|
|
124
|
+
...(format === 'jpeg' && { quality }),
|
|
125
|
+
});
|
|
126
|
+
screenshotBytes = Buffer.from(bytes);
|
|
127
|
+
}
|
|
128
|
+
// Get actual dimensions
|
|
129
|
+
const viewport = page.viewportSize() || { width: width, height: height };
|
|
130
|
+
return {
|
|
131
|
+
bytes: screenshotBytes,
|
|
132
|
+
mime: format === 'jpeg' ? 'image/jpeg' : 'image/png',
|
|
133
|
+
width: viewport.width,
|
|
134
|
+
height: viewport.height,
|
|
135
|
+
source: "screenshot:playwright",
|
|
136
|
+
metadata: {
|
|
137
|
+
url: url || undefined,
|
|
138
|
+
selector: selector || undefined,
|
|
139
|
+
fullPage,
|
|
140
|
+
format,
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
finally {
|
|
145
|
+
await page.close();
|
|
146
|
+
// Close browser if not persistent
|
|
147
|
+
if (!persistent && browser) {
|
|
148
|
+
await browser.close();
|
|
149
|
+
browser = null;
|
|
150
|
+
cachedContext = null;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
// Also export as named export for convenience
|
|
157
|
+
export { screenshot };
|
|
158
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@teamflojo/floimg-screenshot",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Screenshot generator for floimg using Playwright headless browser",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md",
|
|
17
|
+
"LICENSE"
|
|
18
|
+
],
|
|
19
|
+
"keywords": [
|
|
20
|
+
"floimg",
|
|
21
|
+
"screenshot",
|
|
22
|
+
"playwright",
|
|
23
|
+
"headless-browser",
|
|
24
|
+
"web-capture",
|
|
25
|
+
"image-generation"
|
|
26
|
+
],
|
|
27
|
+
"author": "Brett Cooke",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/TeamFlojo/floimg.git",
|
|
32
|
+
"directory": "packages/floimg-screenshot"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"@teamflojo/floimg": "^0.1.0"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"playwright": "^1.48.2"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/node": "^22.10.2",
|
|
42
|
+
"typescript": "^5.7.2",
|
|
43
|
+
"vitest": "^2.1.8",
|
|
44
|
+
"@teamflojo/floimg": "0.1.0"
|
|
45
|
+
},
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=18.0.0"
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "tsc",
|
|
51
|
+
"dev": "tsc --watch",
|
|
52
|
+
"test": "vitest",
|
|
53
|
+
"typecheck": "tsc --noEmit",
|
|
54
|
+
"clean": "rm -rf dist",
|
|
55
|
+
"postinstall": "playwright install chromium"
|
|
56
|
+
}
|
|
57
|
+
}
|