feat: add explained assembly pattern with offline example and QA

This commit is contained in:
jarvis-at-skic committed 2026-10-22 08:40:59 +00:00
1 parent 50eab114ae
commit 563aaf4757
17 files changed
+341 -3

No files matched your search

+27
View File
@@ -0,0 +1,27 @@
"""Build the original assembly example offline from licensed local assets."""
from pathlib import Path
import base64
import hashlib
import json
import re
root = Path(__file__).resolve().parents[1]
for entry in json.loads((root / 'vendor-manifest.json').read_text()):
assert hashlib.sha256((root / entry['path']).read_bytes()).hexdigest() == entry['sha256']
vendor = root / 'assets/vendor'
text = (root / 'assets/mechanism.html').read_text()
js = re.sub(r'//# sourceMappingURL=.*', '', (vendor / 'anime.umd.min.js').read_text())
text = text.replace('/* ANIME_JS */', js)
fonts = ''
for file, weight in [('work-sans.woff2', 400), ('work-sans-bold.woff2', 700)]:
data = base64.b64encode((vendor / file).read_bytes()).decode()
fonts += f"@font-face{{font-family:'Work Sans';font-weight:{weight};src:url(data:font/woff2;base64,{data}) format('woff2');}}"
text = text.replace('/* FONT_CSS */', fonts)
notices = '\n'.join((vendor / f).read_text() for f in ['anime-LICENSE.md', 'work-sans-LICENSE'])
text = text.replace('<!-- LICENSES -->', '<!-- THIRD-PARTY NOTICES\n' + notices.replace('--', '—') + '\n-->')
out = root / 'dist/mechanism-demo-0.2.0.html'
out.parent.mkdir(exist_ok=True)
out.write_text(text)
report = {'file': out.name, 'sha256': hashlib.sha256(out.read_bytes()).hexdigest(), 'interactive_components': 3, 'controls': 4, 'anime': '4.5.0'}
(root / 'dist/mechanism-manifest.json').write_text(json.dumps(report, indent=2) + '\n')
print(json.dumps(report))
+25
View File
@@ -0,0 +1,25 @@
import {chromium} from '@playwright/test';
import fs from 'node:fs';import path from 'node:path';import {pathToFileURL} from 'node:url';import assert from 'node:assert/strict';import crypto from 'node:crypto';
const root=path.resolve(import.meta.dirname,'..'),file=path.join(root,'dist/mechanism-demo-0.2.0.html');
const browser=await chromium.launch({headless:true,...(process.env.CHROMIUM_EXECUTABLE?{executablePath:process.env.CHROMIUM_EXECUTABLE}:{})});
const report={sha256:crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex'),runs:[]};
fs.mkdirSync(path.join(root,'evidence'),{recursive:true});
try{for(const reducedMotion of ['no-preference','reduce']){
const ctx=await browser.newContext({offline:true,reducedMotion,viewport:{width:1280,height:900}}),p=await ctx.newPage(),errors=[],network=[];
p.on('pageerror',e=>errors.push(String(e)));p.on('console',m=>{if(m.type()==='error')errors.push(m.text());});p.on('request',r=>{if(/^https?:/.test(r.url()))network.push(r.url());});
await p.goto(pathToFileURL(file).href);await p.evaluate(()=>document.fonts.ready);
const state=()=>p.evaluate(()=>mechanismQA.state());const settled=()=>p.waitForFunction(()=>!mechanismQA.state().active);
assert.deepEqual((await state()).positions,[460,520,580]);
if(reducedMotion==='no-preference')await p.screenshot({path:path.join(root,'evidence/mechanism-assembled.png')});
await p.locator('#explode').click();if(reducedMotion==='no-preference'){await p.waitForTimeout(150);const s=await state();assert.ok(s.positions[0]<460&&s.positions[0]>200,'actual intermediate motion');}await settled();assert.deepEqual((await state()).positions,[200,520,840]);
const clipped=await p.evaluate(()=>{const r=document.querySelector('svg').getBoundingClientRect();return [...document.querySelectorAll('svg text')].filter(e=>{const b=e.getBoundingClientRect();return b.left<r.left||b.right>r.right||b.top<r.top||b.bottom>r.bottom;}).map(e=>e.textContent);});assert.deepEqual(clipped,[]);
if(reducedMotion==='no-preference')await p.screenshot({path:path.join(root,'evidence/mechanism-exploded.png')});
for(let i=0;i<3;i++){const n=p.locator(`[data-index="${i}"]`);for(const action of ['click','Enter','Space']){if(action==='click')await n.click();else{await n.focus();await p.keyboard.press(action);}await settled();assert.equal((await state()).selected,i);assert.equal(await n.getAttribute('aria-pressed'),'true');assert.equal(await p.locator('#detail-title').textContent(),['Interface','Processing','Storage'][i]);}}
if(reducedMotion==='no-preference')await p.screenshot({path:path.join(root,'evidence/mechanism-inspected.png')});
for(const id of ['reassemble','assemble']){await p.locator('#'+id).click();await settled();assert.deepEqual((await state()).positions,[460,520,580]);assert.equal((await state()).selected,-1);}
await p.locator('#inspect').focus();await p.keyboard.press('Enter');await settled();assert.equal((await state()).selected,1);
await p.evaluate(()=>{for(const id of ['assemble','explode','reassemble','explode','assemble'])document.getElementById(id).click();});await settled();assert.deepEqual((await state()).positions,[460,520,580]);
await p.locator('#explode').click();await p.emulateMedia({reducedMotion:'reduce'});await settled();assert.deepEqual((await state()).positions,[200,520,840]);
await p.locator('#assemble').click();await p.emulateMedia({media:'print'});await p.evaluate(()=>dispatchEvent(new Event('beforeprint')));assert.deepEqual((await state()).positions,[200,520,840]);assert.equal(await p.locator('.controls').isVisible(),false);
assert.deepEqual(errors,[]);assert.deepEqual(network,[]);report.runs.push({reducedMotion,components:3,componentActions:9,controls:4,interruption:true,print:true,clipped,errors,network});await ctx.close();
}fs.writeFileSync(path.join(root,'evidence/mechanism-test-report.json'),JSON.stringify(report,null,2)+'\n');console.log(JSON.stringify(report,null,2));}finally{await browser.close();}
+1 -1
View File
@@ -26,7 +26,7 @@ for(const reducedMotion of ['no-preference','reduce']){
}
assert.equal(testedNodes,6);await page.evaluate(()=>Reveal.slide(0));await page.keyboard.press('ArrowRight');assert.equal(await page.evaluate(()=>Reveal.getIndices().h),1);await page.keyboard.press('ArrowLeft');assert.equal(await page.evaluate(()=>Reveal.getIndices().h),0);
await page.keyboard.press('Escape');assert.equal(await page.evaluate(()=>Reveal.isOverview()),true);await page.keyboard.press('Escape');
await page.locator('#fullscreen').click();await page.waitForFunction(()=>!!document.fullscreenElement);assert.equal(await page.locator('#fullscreen').getAttribute('aria-pressed'),'true');await page.keyboard.press('f');await page.waitForFunction(()=>!document.fullscreenElement);
await page.locator('#fullscreen').click();await page.waitForFunction(()=>!!document.fullscreenElement && document.querySelector('#fullscreen').getAttribute('aria-pressed')==='true');assert.equal(await page.locator('#fullscreen').getAttribute('aria-pressed'),'true');await page.keyboard.press('f');await page.waitForFunction(()=>!document.fullscreenElement);
await page.keyboard.press('f');await page.waitForFunction(()=>!!document.fullscreenElement);await page.locator('#fullscreen').click();await page.waitForFunction(()=>!document.fullscreenElement);
await page.evaluate(()=>Reveal.slide(3));await page.waitForFunction(()=>location.hash.includes('sequence'));await page.reload();await page.waitForFunction(()=>window.deckReady);assert.equal(await page.evaluate(()=>Reveal.getIndices().h),3);
if(reducedMotion==='reduce'){assert.equal(await page.evaluate(()=>presentationQA.active()),0);assert.equal(await page.locator('#motion').getAttribute('aria-pressed'),'false');}