@@ -4,7 +4,7 @@ import { PreviewGeneratorV2RepoAccess } from './PreviewGeneratorV2RepoAccess.js'
44import { PreviewGeneratorV2Capture } from './PreviewGeneratorV2Capture.js' ;
55
66const OUTPUT_NAME = "preview.svg" ;
7- const CAPTURE_TIMEOUT_MARKER = "Capture timeout" ;
7+ const CAPTURE_TIMEOUT_MARKER = PreviewGeneratorV2Capture . CAPTURE_TIMEOUT_MARKER ;
88
99const runtimeParams = new URLSearchParams ( window . location . search ) ;
1010const isRuntimeMode = runtimeParams . get ( "mode" ) === "runtime" ;
@@ -18,204 +18,15 @@ const logger = new PreviewGeneratorV2Logger({
1818 statusEl : ui . statusLog . getStatusElement ( ) ,
1919 logEl : ui . statusLog . getLogElement ( )
2020} ) ;
21- const frame = ui . previewFrame . getFrame ( ) ;
21+ const capture = new PreviewGeneratorV2Capture ( {
22+ ui,
23+ frame : ui . previewFrame . getFrame ( )
24+ } ) ;
2225
2326function sleep ( ms ) {
2427 return new Promise ( resolve => setTimeout ( resolve , ms ) ) ;
2528}
2629
27- function initializeRuntimeCaptureMode ( ) {
28- const samplePath = String ( runtimeParams . get ( "sample" ) || "" ) . trim ( ) ;
29- const width = Math . max ( 1 , Number ( runtimeParams . get ( "w" ) || 640 ) ) ;
30- const height = Math . max ( 1 , Number ( runtimeParams . get ( "h" ) || 360 ) ) ;
31- const settleMs = Math . max ( 0 , Number ( runtimeParams . get ( "settle" ) || 3000 ) ) ;
32- const timeoutMs = Math . max ( 1000 , Number ( runtimeParams . get ( "timeout" ) || 12000 ) ) ;
33- const captureModeParam = String ( runtimeParams . get ( "capture" ) || "canvasOnly" ) . trim ( ) ;
34- const runtimeCaptureMode = / ^ f u l l s c r e e n / i. test ( captureModeParam ) ? "fullscreen" : "canvasOnly" ;
35-
36- function setStatus ( status ) {
37- document . body . setAttribute ( "data-capture-status" , status ) ;
38- document . title = "capture:" + status ;
39- }
40-
41- const bodyChildren = Array . from ( document . body . children ) ;
42- for ( const node of bodyChildren ) {
43- node . style . display = "none" ;
44- }
45- document . body . style . margin = "0" ;
46- document . body . style . background = "#000" ;
47- document . body . style . overflow = "hidden" ;
48-
49- frame . style . display = "block" ;
50- frame . style . position = "fixed" ;
51- frame . style . width = "1px" ;
52- frame . style . height = "1px" ;
53- frame . style . left = "-10000px" ;
54- frame . style . top = "-10000px" ;
55- frame . style . border = "0" ;
56- frame . style . visibility = "hidden" ;
57- frame . setAttribute ( "sandbox" , "allow-scripts allow-same-origin" ) ;
58-
59- const outputCanvas = document . createElement ( "canvas" ) ;
60- outputCanvas . id = "runtimeCaptureSurface" ;
61- outputCanvas . width = width ;
62- outputCanvas . height = height ;
63- outputCanvas . style . display = "block" ;
64- outputCanvas . style . width = "100vw" ;
65- outputCanvas . style . height = "100vh" ;
66- outputCanvas . style . background = "#000" ;
67- document . body . appendChild ( outputCanvas ) ;
68-
69- const ctx = outputCanvas . getContext ( "2d" ) ;
70- if ( ! ctx ) {
71- setStatus ( "error" ) ;
72- return ;
73- }
74-
75- function writeLabel ( text , color ) {
76- ctx . fillStyle = "#0b1020" ;
77- ctx . fillRect ( 0 , 0 , width , height ) ;
78- ctx . strokeStyle = "#334e7a" ;
79- ctx . lineWidth = 2 ;
80- ctx . strokeRect ( 10 , 10 , width - 20 , height - 20 ) ;
81- ctx . fillStyle = color ;
82- ctx . font = "600 18px monospace" ;
83- ctx . fillText ( text , 20 , 40 ) ;
84- }
85-
86- function drawSourceCanvas ( sourceCanvas ) {
87- const sw = sourceCanvas . width || sourceCanvas . clientWidth || width ;
88- const sh = sourceCanvas . height || sourceCanvas . clientHeight || height ;
89- if ( ! sw || ! sh ) {
90- throw new Error ( "Canvas has invalid size." ) ;
91- }
92-
93- const scale = Math . max ( width / sw , height / sh ) ;
94- const dw = sw * scale ;
95- const dh = sh * scale ;
96- const dx = ( width - dw ) * 0.5 ;
97- const dy = ( height - dh ) * 0.5 ;
98-
99- ctx . fillStyle = "#000" ;
100- ctx . fillRect ( 0 , 0 , width , height ) ;
101- ctx . drawImage ( sourceCanvas , dx , dy , dw , dh ) ;
102- }
103-
104- function fail ( message ) {
105- writeLabel ( message , "#ff9ca8" ) ;
106- setStatus ( "error" ) ;
107- }
108-
109- if ( ! samplePath ) {
110- fail ( "Missing sample parameter" ) ;
111- return ;
112- }
113-
114- writeLabel ( "Loading sample..." , "#a5c0e6" ) ;
115- setStatus ( "loading" ) ;
116-
117- const startTime = Date . now ( ) ;
118- let settled = false ;
119-
120- async function captureFullScreenCanvas ( frameDoc ) {
121- const html2canvasFn = window . html2canvas ;
122- if ( typeof html2canvasFn !== "function" ) {
123- throw new Error ( "Full Screen capture failed: html2canvas is unavailable. Use Canvas Only or ensure html2canvas loads before using Full Screen." ) ;
124- }
125-
126- const frameBody = frameDoc . body ;
127- if ( ! frameBody ) {
128- throw new Error ( "Full Screen capture failed because the target document has no body." ) ;
129- }
130-
131- try {
132- const captureSize = PreviewGeneratorV2Capture . getAvailableFullScreenCaptureSize ( document , window , width , height ) ;
133- return await html2canvasFn ( frameBody , {
134- backgroundColor : "#000000" ,
135- useCORS : true ,
136- allowTaint : true ,
137- logging : false ,
138- width : captureSize . width ,
139- height : captureSize . height ,
140- windowWidth : captureSize . width ,
141- windowHeight : captureSize . height ,
142- scrollX : 0 ,
143- scrollY : 0 ,
144- onclone : ( clonedDoc ) => {
145- sanitizeClonedToolDocument ( clonedDoc ) ;
146- }
147- } ) ;
148- } catch ( error ) {
149- throw new Error ( `Full Screen capture failed: ${ error . message } ` ) ;
150- }
151- }
152-
153- async function attemptCapture ( ) {
154- try {
155- const frameDoc = frame . contentDocument ;
156- if ( ! frameDoc ) {
157- throw new Error ( "Frame document unavailable." ) ;
158- }
159-
160- let sourceCanvas ;
161- if ( runtimeCaptureMode === "fullscreen" ) {
162- sourceCanvas = await captureFullScreenCanvas ( frameDoc ) ;
163- } else {
164- sourceCanvas = frameDoc . querySelector ( "canvas" ) ;
165- }
166-
167- if ( ! sourceCanvas ) {
168- if ( Date . now ( ) - startTime > timeoutMs ) {
169- fail ( "Canvas not found" ) ;
170- return ;
171- }
172- requestAnimationFrame ( ( ) => {
173- attemptCapture ( ) ;
174- } ) ;
175- return ;
176- }
177-
178- if ( ! settled ) {
179- settled = true ;
180- setTimeout ( ( ) => {
181- attemptCapture ( ) ;
182- } , settleMs ) ;
183- return ;
184- }
185-
186- drawSourceCanvas ( sourceCanvas ) ;
187- setStatus ( "ready" ) ;
188- } catch ( error ) {
189- if ( runtimeCaptureMode === "fullscreen" ) {
190- fail ( error . message ) ;
191- return ;
192- }
193-
194- if ( Date . now ( ) - startTime > timeoutMs ) {
195- fail ( "Capture timeout" ) ;
196- return ;
197- }
198- requestAnimationFrame ( ( ) => {
199- attemptCapture ( ) ;
200- } ) ;
201- }
202- }
203-
204- frame . addEventListener ( "load" , ( ) => {
205- requestAnimationFrame ( ( ) => {
206- attemptCapture ( ) ;
207- } ) ;
208- } , { once : true } ) ;
209-
210- frame . src = samplePath ;
211-
212- setTimeout ( ( ) => {
213- if ( document . body . getAttribute ( "data-capture-status" ) !== "ready" ) {
214- fail ( "Capture timeout" ) ;
215- }
216- } , timeoutMs + settleMs + 800 ) ;
217- }
218-
21930function normalizeSlashes ( value ) {
22031 return String ( value || "" )
22132 . replaceAll ( "/" , "\\" )
0 commit comments