// regions.mjs — draw a region by clicking, name and save it, watch it refine, tiles switch, delete it
const P = acts => [{ type: 'pointer', id: 'mouse', parameters: { pointerType: 'mouse' }, actions: acts }];
const click = (x, y) => P([{ type: 'pointerMove', x, y }, { type: 'pointerDown', button: 0 }, { type: 'pointerUp', button: 0 }]);
const wait = `const a = window.mapViewer; for (let i = 0; i < 400; i++) { await new Promise(r => setTimeout(r, 500));
if (['ready', 'failed'].includes(a.regions.status?.state)) break; }
return { state: a.regions.status?.state, n: a.regions.regions.length, url: a.meta.tiles.url, url0: window.__url0,
line: a.view.lines.objects.has('regions'), text: document.getElementById('tab-regions').textContent };`;
const corner = (x, y) => ({ name: `outline point ${x},${y}`, actions: click(x, y), settle: 300,
js: 'return window.mapViewer.regions.points?.length ?? -1;', expect: v => v >= 1 });
export const steps = [
{ name: 'draw mode', wait: 4000, expect: v => v === 'region',
js: `const a = window.mapViewer; window.__url0 = a.meta.tiles.url; a.view.setState({ lat: 46.5, lon: 9.8, alt: 0.08 });
await new Promise(r => setTimeout(r, 500)); a.showTab('regions');
[...document.querySelectorAll('#tab-regions button')].find(b => b.textContent.includes('Draw region')).click(); return a.mode;` },
corner(500, 350), corner(700, 350),
{ name: 'Esc closing the controls card keeps the outline', expect: v => v[0] === true && v[1] === 2 && v[2] === 'region',
js: `const k = key => dispatchEvent(new KeyboardEvent('keydown', { key, shiftKey: key === '?' }));
k('?'); k('Escape'); const a = window.mapViewer;
return [document.getElementById('help').hidden, a.regions.points?.length ?? -1, a.mode];` },
{ name: 'clicking the Regions tab again keeps drawing', expect: v => v[0] === 'region' && v[1] === 2,
js: `const a = window.mapViewer; document.querySelector('#panel nav [data-tab=regions]').click();
return [a.mode, a.regions.points?.length ?? -1];` },
corner(700, 500), corner(500, 500),
{ name: 'a double-click closes it too (no duplicate point, no re-centring)',
expect: v => v[0] === true && v[1] === 5 && v[2] < 1e-9,
actions: P([{ type: 'pointerMove', x: 560, y: 560 }, { type: 'pointerDown', button: 0 }, { type: 'pointerUp', button: 0 },
{ type: 'pointerDown', button: 0 }, { type: 'pointerUp', button: 0 }]), settle: 300,
js: `const r = window.mapViewer.regions, s = window.mapViewer.view.getState();
const out = [r.closed, r.points.length, Math.abs(s.lat - 46.5) + Math.abs(s.lon - 9.8)]; r.closed = false; r.points.pop();
window.mapViewer.setMode('region'); return out;` },
{ name: 'clicking the first point closes the outline', actions: click(503, 352), settle: 300,
js: `return [window.mapViewer.regions.closed, window.mapViewer.mode, !!document.querySelector('form.region input')];`,
expect: ([c, m, i]) => c && m === 'inspect' && i },
{ name: 'name + save starts the refinement', expect: v => v.n === 1,
js: `const i = document.querySelector('form.region input'); i.value = 'Smoke region'; i.dispatchEvent(new Event('input'));
document.querySelector('form.region button[type=submit]').click(); await new Promise(r => setTimeout(r, 800));
return { n: window.mapViewer.regions.regions.length };` },
{ name: 'refines, tiles switch, outline drawn', js: wait,
expect: v => v.state === 'ready' && v.url !== v.url0 && v.line && /up to date/.test(v.text) && /· refined/.test(v.text) },
{ name: 'delete removes it and rebuilds', expect: v => v.n === 0 && v.state === 'ready',
js: `window.confirm = () => true; [...document.querySelectorAll('#tab-regions button')].find(b => b.textContent === 'Delete').click();
await new Promise(r => setTimeout(r, 800));` + wait },
];