1
1
import p from "puppeteer"
2
2
import chalk from "chalk"
3
+ import path from "path"
3
4
4
5
function sleep ( time ) {
5
6
return new Promise ( ( resolve , reject ) => {
@@ -9,7 +10,7 @@ function sleep(time) {
9
10
} )
10
11
}
11
12
12
- export async function pdf ( url , pdf_path , options , beforeClose = async ( ) => { } ) {
13
+ export async function pdf ( url , pdf_path , options , screenshot_dir , screenshot_options , { beforeClose = async ( ) => { } } = { } ) {
13
14
const browser = await p . launch ( )
14
15
console . log ( "Initiated headless browser" )
15
16
const page = await browser . newPage ( )
@@ -24,34 +25,66 @@ export async function pdf(url, pdf_path, options, beforeClose = async () => {})
24
25
height : 1000 ,
25
26
} )
26
27
27
- while ( true ) {
28
- const queued = await page . evaluate ( `Array.from(document.getElementsByClassName('queued')).map(x => x.id)` )
29
- const running = await page . evaluate ( `Array.from(document.getElementsByClassName('running')).map(x => x.id)` )
30
- const cells = await page . evaluate ( `Array.from(document.getElementsByTagName('pluto-cell')).map(x => x.id)` )
31
- const bodyClasses = await page . evaluate ( `document.body.getAttribute('class')` )
32
-
33
- if ( running . length > 0 ) {
34
- process . stdout . write ( `\rRunning cell ${ chalk . yellow ( `${ cells . length - queued . length } /${ cells . length } ` ) } ${ chalk . cyan ( `[${ running [ 0 ] } ]` ) } ` )
35
- }
36
-
37
- if ( ! ( bodyClasses . includes ( "loading" ) || queued . length > 0 || running . length > 0 ) ) {
38
- process . stdout . write ( `\rRunning cell ${ chalk . yellow ( `${ cells . length } /${ cells . length } ` ) } ` )
39
- console . log ( )
40
- break
41
- }
42
-
43
- await sleep ( 250 )
44
- }
28
+ await waitForPlutoBusy ( page , false , { timeout : 30 * 1000 } )
45
29
46
30
console . log ( "Exporting as pdf..." )
47
31
await page . pdf ( {
48
32
path : pdf_path ,
49
33
...options ,
50
34
} )
35
+ if ( screenshot_dir != null ) {
36
+ await screenshot_cells ( page , screenshot_dir , screenshot_options )
37
+ }
51
38
52
39
console . log ( chalk . green ( "Exported ✓" ) + " ... cleaning up" )
53
40
54
41
await beforeClose ( )
55
-
56
42
await browser . close ( )
57
43
}
44
+
45
+ /**
46
+ * @param {p.Page } page
47
+ * @param {string } screenshot_dir
48
+ */
49
+ async function screenshot_cells ( page , screenshot_dir , { outputOnly, scale } ) {
50
+ const cells = /** @type {String[] } */ ( await page . evaluate ( `Array.from(document.querySelectorAll('pluto-cell')).map(x => x.id)` ) )
51
+
52
+ for ( let cell_id of cells ) {
53
+ const cell = await page . $ ( `[id="${ cell_id } "]${ outputOnly ? " > pluto-output" : "" } ` )
54
+ if ( cell ) {
55
+ await cell . scrollIntoView ( )
56
+ const rect = await cell . boundingBox ( )
57
+ if ( rect == null ) {
58
+ throw new Error ( `Cell ${ cell_id } is not visible` )
59
+ }
60
+ const imgpath = path . join ( screenshot_dir , `${ cell_id } .png` )
61
+
62
+ await cell . screenshot ( { path : imgpath , clip : { ...rect , scale } , omitBackground : false } )
63
+ console . log ( `Screenshot ${ cell_id } saved to ${ imgpath } ` )
64
+ }
65
+ }
66
+ }
67
+
68
+ const timeout = ( delay ) =>
69
+ new Promise ( ( r ) => {
70
+ setTimeout ( r , delay )
71
+ } )
72
+
73
+ const waitForPlutoBusy = async ( page , iWantBusiness , options ) => {
74
+ await timeout ( 1000 )
75
+ await page . waitForFunction (
76
+ ( iWantBusiness ) => {
77
+ let quiet = //@ts -ignore
78
+ ( document ?. body ?. _update_is_ongoing ?? false ) === false &&
79
+ //@ts -ignore
80
+ ( document ?. body ?. _js_init_set ?. size ?? 0 ) === 0 &&
81
+ document ?. body ?. classList ?. contains ( "loading" ) === false &&
82
+ document ?. querySelector ( `pluto-cell.running, pluto-cell.queued, pluto-cell.internal_test_queued` ) == null
83
+
84
+ return iWantBusiness ? ! quiet : quiet
85
+ } ,
86
+ options ,
87
+ iWantBusiness
88
+ )
89
+ await timeout ( 1000 )
90
+ }
0 commit comments