SOURCE / PINNED RELEASE
Made of little things.
Body Journal
- Release
- e8049f8da4e2…
- Author-recorded commit
- 61c04bf0e8aa…
- License
- LICENSE
- Author’s source reference
- nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-6c4554844c7
Archive hash verified: 4369d778da046a8b…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
import test from 'node:test';
import assert from 'node:assert/strict';
import {readFileSync} from 'node:fs';
import {runInNewContext} from 'node:vm';
import ts from 'typescript';
const module={exports:{}};
runInNewContext(ts.transpileModule(readFileSync('src/scene-gestures.ts','utf8'),{compilerOptions:{module:ts.ModuleKind.CommonJS,target:ts.ScriptTarget.ES2022}}).outputText,{module,exports:module.exports});
const {SceneGestures}=module.exports;
const pointer=(id,x,y,type='touch',button=0)=>({pointerId:id,clientX:x,clientY:y,pointerType:type,button,buttons:button===2?2:1});
function setup(){const events=[];return {events,g:new SceneGestures(Object.fromEntries(['start','move','end','hover','rotate','zoom'].map(name=>[name,(...args)=>events.push([name,...args])])))};}
test('right drag rotates without starting the selected tool; left drag still invokes it',()=>{
const {g,events}=setup();g.down(pointer(1,10,20,'mouse',2));g.move(pointer(1,40,15,'mouse',2));g.up(pointer(1,40,15,'mouse',2));
assert.deepEqual(events.filter(e=>e[0]!=='end'),[['rotate',30,-5]]);
events.length=0;g.down(pointer(1,10,20,'mouse'));g.move(pointer(1,40,15,'mouse'));g.up(pointer(1,40,15,'mouse'));
assert.deepEqual(events.map(e=>e[0]),['start','move','end']);
});
test('two-finger translation rotates, preserves zoom overall and leaves no stray paint tap',()=>{
const {g,events}=setup();g.down(pointer(1,20,20));g.down(pointer(2,100,20));
g.move(pointer(1,40,30));g.move(pointer(2,120,30));g.up(pointer(2,120,30));g.move(pointer(1,70,50));g.up(pointer(1,70,50));
assert.equal(events.filter(e=>e[0]==='start'||e[0]==='move').length,0);
assert.deepEqual(events.filter(e=>e[0]==='rotate').map(e=>e.slice(1)),[[10,5],[10,5]]);
assert.ok(Math.abs(events.filter(e=>e[0]==='zoom').reduce((n,e)=>n+e[1],0))<1e-10);
g.down(pointer(3,10,10));g.up(pointer(3,10,10));assert.equal(events.filter(e=>e[0]==='start').length,1,'Next one-finger tap uses the selected tool');
});
test('pinch zoom coexists with rotation and cancels a preceding one-finger tool drag',()=>{
const {g,events}=setup();g.down(pointer(1,20,20));g.move(pointer(1,30,20));
assert.deepEqual(events.map(e=>e[0]),['start','move']);
g.down(pointer(2,100,20));events.length=0;g.move(pointer(2,140,20));
assert.deepEqual(events,[['rotate',20,0],['zoom',-.32]]);
});
test('cancel and lost focus discard pending touches and release gesture state',()=>{
const {g,events}=setup();g.down(pointer(1,20,20));g.up(pointer(1,20,20),true);
g.down(pointer(1,20,20));g.down(pointer(2,100,20));g.reset();
assert.equal(events.filter(e=>e[0]==='start').length,0);
g.down(pointer(1,20,20,'mouse',2));g.move(pointer(1,30,30,'mouse',2));assert.deepEqual(events.at(-1),['rotate',10,10]);
});
test('trackpad scroll and native pinch zoom; secondary click-drag is the rotation override',()=>{
const {g,events}=setup();const w={deltaX:12,deltaY:30,deltaMode:0,ctrlKey:false,shiftKey:false};
g.wheel(w);g.wheel({...w,ctrlKey:true});g.wheel({...w,shiftKey:true});
assert.deepEqual(events,[['zoom',.06],['zoom',.06],['zoom',.06]]);
});
