WIP: poster capture no longer dies on a late idle; gate waits 120s
This commit is contained in:
parent
178d7ac3e8
commit
e50fc858ba
2 changed files with 18 additions and 12 deletions
|
|
@ -410,24 +410,30 @@ async function _spawnHiddenMap(sourceMap, w, h, opts = {}) {
|
|||
return { hiddenMap, restoreDpr, container };
|
||||
}
|
||||
|
||||
// The hidden map's 'idle' can arrive late or never on a heavy render (measured 2026-09-25: an
|
||||
// Arabic Letter poster, RTL shaping + a 2x canvas, missed a 15s idle and the export gave NO file).
|
||||
// So: at IDLE_TIMEOUT_MS, capture if every tile is in (something else is only repainting); keep
|
||||
// waiting to IDLE_HARD_MS otherwise, then capture what is drawn with a warning. A poster whose last
|
||||
// tiles are missing is still a file; a rejected promise is a dead Export button.
|
||||
const IDLE_HARD_MS = 60000;
|
||||
function _waitForIdle(map) {
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise((resolve) => {
|
||||
let settled = false;
|
||||
const t = setTimeout(() => {
|
||||
const timers = [];
|
||||
const finish = (why) => {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
reject(new Error('useMapPoster: hidden map idle timeout'));
|
||||
}, IDLE_TIMEOUT_MS);
|
||||
const finish = () => {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
clearTimeout(t);
|
||||
timers.forEach(clearTimeout);
|
||||
if (why) console.warn('[useMapPoster] ' + why);
|
||||
requestAnimationFrame(() => resolve());
|
||||
};
|
||||
const tilesIn = () => { try { return typeof map.areTilesLoaded === 'function' && map.areTilesLoaded(); } catch (_) { return false; } };
|
||||
timers.push(setTimeout(() => { if (tilesIn()) finish(); }, IDLE_TIMEOUT_MS));
|
||||
timers.push(setTimeout(() => finish('hidden map not idle after ' + IDLE_HARD_MS / 1000 + 's; capturing what is drawn'), IDLE_HARD_MS));
|
||||
if (map.loaded() && !map.isMoving()) {
|
||||
finish();
|
||||
} else {
|
||||
map.once('idle', finish);
|
||||
map.once('idle', () => finish());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
* The drawer's "Export poster…" does not count: the drawer is CLOSED by default (translated off
|
||||
* screen), so it costs an extra click to reach — measured 2026-09-25, and in Arabic the button
|
||||
* could not be clicked at all. The toolbar button must be on screen with no preparation;
|
||||
* 2. a download arrives within 90s, WITHOUT any further click;
|
||||
* 2. a download arrives within 120s, WITHOUT any further click;
|
||||
* 3. the file is a real PNG (signature) of the print size (US Letter at 200 DPI = 1700x2200);
|
||||
* 4. 0 page errors.
|
||||
*
|
||||
|
|
@ -71,7 +71,7 @@ try {
|
|||
const label = ((await btn.getAttribute('title', { timeout: 20000 }).catch(() => '')) || '').trim()
|
||||
let clicks = 0
|
||||
try { await btn.click({ timeout: 20000 }); clicks = 1 } catch (e) { errs.push('could not click: ' + String(e.message || e).split('\n')[0]) }
|
||||
for (let i = 0; i < 90 && !downloads.length; i++) await p.waitForTimeout(1000)
|
||||
for (let i = 0; i < 120 && !downloads.length; i++) await p.waitForTimeout(1000)
|
||||
let file = null, info = ''
|
||||
if (downloads.length) {
|
||||
file = downloads[0].suggestedFilename()
|
||||
|
|
@ -83,7 +83,7 @@ try {
|
|||
if (!png || w < 1600 || h < 2100) errs.push(`file is not the expected print PNG (${info})`)
|
||||
}
|
||||
const ok = clicks === 1 && !!file && !errs.length
|
||||
console.log(`${ok ? 'PASS' : 'FAIL'} [${where} ${lang}] "${label}" · 1 click → ${file ? `${file} (${info})` : 'NO FILE within 90s'}${errs.length ? ' · ' + [...new Set(errs)].slice(0, 3).join(' | ') : ' · 0 errors'}`)
|
||||
console.log(`${ok ? 'PASS' : 'FAIL'} [${where} ${lang}] "${label}" · 1 click → ${file ? `${file} (${info})` : 'NO FILE within 120s'}${errs.length ? ' · ' + [...new Set(errs)].slice(0, 3).join(' | ') : ' · 0 errors'}`)
|
||||
if (!ok) fails.push(`${where} ${lang}`)
|
||||
await ctx.close()
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue