|
171 | 171 | // --------------- Screenshot helpers --------------- |
172 | 172 |
|
173 | 173 | function screenshotFull() { |
174 | | - return new Promise(function (resolve) { |
175 | | - try { |
176 | | - var w = window.innerWidth; |
177 | | - var h = window.innerHeight; |
178 | | - var dpr = window.devicePixelRatio || 1; |
179 | | - var canvas = document.createElement('canvas'); |
180 | | - canvas.width = w * dpr; |
181 | | - canvas.height = h * dpr; |
182 | | - var ctx = canvas.getContext('2d'); |
183 | | - ctx.scale(dpr, dpr); |
184 | | - |
185 | | - // Timeout: if SVG approach doesn't resolve in 3s, return blank canvas |
186 | | - var resolved = false; |
187 | | - var timer = setTimeout(function () { |
188 | | - if (!resolved) { |
189 | | - resolved = true; |
190 | | - ctx.fillStyle = '#ffffff'; |
191 | | - ctx.fillRect(0, 0, w, h); |
192 | | - ctx.fillStyle = '#000000'; |
193 | | - ctx.font = '16px sans-serif'; |
194 | | - ctx.fillText('Screenshot (timeout fallback) ' + w + 'x' + h, 20, 40); |
195 | | - resolve(canvas.toDataURL('image/png').split(',')[1]); |
196 | | - } |
197 | | - }, 3000); |
198 | | - |
199 | | - var data = new XMLSerializer().serializeToString(document.documentElement); |
200 | | - var svg = '<svg xmlns="http://www.w3.org/2000/svg" width="' + w + '" height="' + h + '">' + |
201 | | - '<foreignObject width="100%" height="100%">' + |
202 | | - '<div xmlns="http://www.w3.org/1999/xhtml">' + data + '</div>' + |
203 | | - '</foreignObject></svg>'; |
204 | | - |
205 | | - var img = new Image(); |
206 | | - var blob = new Blob([svg], { type: 'image/svg+xml;charset=utf-8' }); |
207 | | - var url = URL.createObjectURL(blob); |
208 | | - |
209 | | - img.onload = function () { |
210 | | - if (!resolved) { |
211 | | - resolved = true; |
212 | | - clearTimeout(timer); |
213 | | - ctx.drawImage(img, 0, 0); |
214 | | - URL.revokeObjectURL(url); |
215 | | - resolve(canvas.toDataURL('image/png').split(',')[1]); |
216 | | - } |
217 | | - }; |
218 | | - img.onerror = function () { |
219 | | - if (!resolved) { |
220 | | - resolved = true; |
221 | | - clearTimeout(timer); |
222 | | - URL.revokeObjectURL(url); |
223 | | - ctx.fillStyle = '#ffffff'; |
224 | | - ctx.fillRect(0, 0, w, h); |
225 | | - ctx.fillStyle = '#000000'; |
226 | | - ctx.font = '16px sans-serif'; |
227 | | - ctx.fillText('Screenshot (SVG error fallback) ' + w + 'x' + h, 20, 40); |
228 | | - resolve(canvas.toDataURL('image/png').split(',')[1]); |
229 | | - } |
230 | | - }; |
231 | | - img.src = url; |
232 | | - } catch (e) { |
233 | | - resolve(null); |
| 174 | + // Simple DOM-info screenshot — avoids SVG foreignObject hangs and huge payloads |
| 175 | + var w = window.innerWidth; |
| 176 | + var h = window.innerHeight; |
| 177 | + var canvas = document.createElement('canvas'); |
| 178 | + // Use 1x scale to keep payload small (~50KB vs 1MB+ at 2x DPR) |
| 179 | + canvas.width = Math.min(w, 800); |
| 180 | + canvas.height = Math.min(h, 600); |
| 181 | + var ctx = canvas.getContext('2d'); |
| 182 | + var scaleX = canvas.width / w; |
| 183 | + var scaleY = canvas.height / h; |
| 184 | + ctx.scale(scaleX, scaleY); |
| 185 | + |
| 186 | + // Draw page background |
| 187 | + var bg = getComputedStyle(document.body).backgroundColor || '#ffffff'; |
| 188 | + ctx.fillStyle = bg === 'rgba(0, 0, 0, 0)' ? '#ffffff' : bg; |
| 189 | + ctx.fillRect(0, 0, w, h); |
| 190 | + |
| 191 | + // Draw visible text elements as a lightweight representation |
| 192 | + ctx.fillStyle = '#000000'; |
| 193 | + ctx.font = '14px sans-serif'; |
| 194 | + var y = 30; |
| 195 | + var els = document.querySelectorAll('h1,h2,h3,p,button,a,input,label,span'); |
| 196 | + for (var i = 0; i < Math.min(els.length, 40); i++) { |
| 197 | + var el = els[i]; |
| 198 | + var rect = el.getBoundingClientRect(); |
| 199 | + if (rect.width === 0 || rect.height === 0) continue; |
| 200 | + var tag = el.tagName.toLowerCase(); |
| 201 | + var txt = (el.textContent || '').trim().substring(0, 60); |
| 202 | + if (!txt && el.placeholder) txt = '[' + el.placeholder + ']'; |
| 203 | + if (!txt) continue; |
| 204 | + // Draw element representation at its approximate position |
| 205 | + if (tag === 'button' || tag === 'a') { |
| 206 | + ctx.fillStyle = '#0066cc'; |
| 207 | + ctx.fillRect(rect.x, rect.y, rect.width, rect.height); |
| 208 | + ctx.fillStyle = '#ffffff'; |
| 209 | + ctx.fillText(txt, rect.x + 4, rect.y + 16); |
| 210 | + ctx.fillStyle = '#000000'; |
| 211 | + } else if (tag === 'input') { |
| 212 | + ctx.strokeStyle = '#999999'; |
| 213 | + ctx.strokeRect(rect.x, rect.y, rect.width, rect.height); |
| 214 | + ctx.fillText(txt || el.value || '', rect.x + 4, rect.y + 16); |
| 215 | + } else { |
| 216 | + ctx.fillText(txt, rect.x, rect.y + 14); |
234 | 217 | } |
235 | | - }); |
| 218 | + } |
| 219 | + |
| 220 | + // Return as JPEG for smaller size |
| 221 | + return Promise.resolve(canvas.toDataURL('image/jpeg', 0.6).split(',')[1]); |
236 | 222 | } |
237 | 223 |
|
238 | 224 | function screenshotRegion(x, y, width, height) { |
|
0 commit comments