feat: publish researched offline HTML presentation skill

This commit is contained in:
jarvis-at-skic committed 2026-10-21 20:27:50 +00:00
commit 50eab114ae
35 files changed
+1201

No files matched your search

+30
View File
@@ -0,0 +1,30 @@
"""Build an offline, single-file deck with no package install or network."""
from pathlib import Path
import base64, hashlib, json, re
R=Path(__file__).resolve().parents[1]
def build():
vendor=R/'assets/vendor'; s=(R/'assets/starter.html').read_text()
manifest=R/'vendor-manifest.json'
if manifest.exists():
for entry in json.loads(manifest.read_text()):
assert hashlib.sha256((R/entry['path']).read_bytes()).hexdigest()==entry['sha256'], entry['path']
nodes=[]
labels=['People','Process','Platform','Quality','Cost','Risk']
details=['People: who owns the decision?','Process: how does work move?','Platform: what enables delivery?','Quality: what evidence proves fitness?','Cost: what constraint shapes the choice?','Risk: what could invalidate the plan?']
for i,(label,detail) in enumerate(zip(labels,details)):
x=[250,550,850][i%3];y=65 if i<3 else 305
nodes.append(f'<g role="button" tabindex="0" aria-label="{label}" aria-pressed="false" data-node="{i}" data-detail="{detail}"><rect x="{x-110}" y="{y-32}" width="220" height="64"/><text x="{x}" y="{y}">{label}</text></g>')
s=s.replace('<!-- NODES -->','\n'.join(nodes))
for marker,file in [('REVEAL_CSS','reveal.css'),('REVEAL_JS','reveal.js'),('ANIME_JS','anime.umd.min.js')]:
text=(vendor/file).read_text();text=re.sub(r'//# sourceMappingURL=.*','',text)
s=s.replace('/* '+marker+' */',text)
fonts=''
for file,weight in [('work-sans.woff2',400),('work-sans-bold.woff2',700)]:
fonts+=f"@font-face{{font-family:'Work Sans';font-weight:{weight};font-style:normal;font-display:block;src:url(data:font/woff2;base64,{base64.b64encode((vendor/file).read_bytes()).decode()}) format('woff2');}}"
s=s.replace('/* FONT_CSS */',fonts).replace('/* APP_JS */',(R/'assets/app.js').read_text())
licenses='\n'.join((vendor/f).read_text() for f in ['reveal-LICENSE','anime-LICENSE.md','work-sans-LICENSE'])
s=s.replace('<!-- LICENSES -->','<!-- THIRD-PARTY NOTICES\n'+licenses.replace('--','—')+'\n-->')
out=R/'dist';out.mkdir(exist_ok=True);p=out/'presentation-html-demo-0.1.0.html';p.write_text(s)
report={'file':p.name,'sha256':hashlib.sha256(p.read_bytes()).hexdigest(),'bytes':p.stat().st_size,'slides':6,'interactive_nodes':6,'reveal':'6.0.2','anime':'4.5.0'}
(out/'manifest.json').write_text(json.dumps(report,indent=2)+'\n');print(json.dumps(report))
if __name__=='__main__':build()
+37
View File
@@ -0,0 +1,37 @@
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,'..');const file=process.env.DECK_PATH||path.join(root,'dist/presentation-html-demo-0.1.0.html');
const executablePath=process.env.CHROMIUM_EXECUTABLE;
const browser=await chromium.launch({headless:true,...(executablePath?{executablePath}:{})});
const report={file,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 context=await browser.newContext({viewport:{width:1440,height:900},reducedMotion,offline:true});const page=await context.newPage();const errors=[],requests=[];
page.on('pageerror',e=>errors.push(String(e)));page.on('console',m=>{if(m.type()==='error')errors.push(m.text());});page.on('request',r=>{if(/^https?:/.test(r.url()))requests.push(r.url());});
await page.goto(pathToFileURL(file).href);await page.waitForFunction(()=>window.deckReady);await page.evaluate(()=>document.fonts.ready);
assert.equal(await page.evaluate(()=>document.fonts.check('28px "Work Sans"')),true);
assert.equal(await page.evaluate(()=>getComputedStyle(document.body).backgroundColor),'rgb(16, 30, 37)');
assert.equal(await page.evaluate(()=>getComputedStyle(document.querySelector('.reveal')).color),'rgb(244, 243, 237)');
const slides=await page.locator('.slides>section').count();assert.equal(slides,6);
let testedNodes=0;
for(let i=0;i<slides;i++){
await page.evaluate(i=>Reveal.slide(i),i);await page.waitForTimeout(900);
const clipped=await page.evaluate(()=>{const s=Reveal.getCurrentSlide(),r=s.getBoundingClientRect();return [...s.querySelectorAll('h1,h2,p,strong,svg text,button')].filter(e=>{const b=e.getBoundingClientRect();return b.left<r.left-2||b.right>r.right+2||b.top<r.top-2||b.bottom>r.bottom+2;}).map(e=>e.textContent);});assert.deepEqual(clipped,[],'Clipping slide '+i);
const nodes=page.locator('.present [data-node]');
for(let j=0;j<await nodes.count();j++){
const n=nodes.nth(j);await n.focus();await page.keyboard.press('Enter');assert.equal(await n.getAttribute('aria-pressed'),'true');assert.equal(await page.locator('#detail').textContent(),await n.getAttribute('data-detail'));await page.keyboard.press('Space');assert.equal(await n.getAttribute('aria-pressed'),'false');await n.click();assert.equal(await n.getAttribute('aria-pressed'),'true');await n.click();testedNodes++;
}
if(reducedMotion==='no-preference')await page.screenshot({path:path.join(root,`evidence/slide-${String(i+1).padStart(2,'0')}.png`)});
}
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.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');}
await page.emulateMedia({media:'print'});await page.evaluate(()=>dispatchEvent(new Event('beforeprint')));assert.equal(await page.evaluate(()=>getComputedStyle(document.querySelector('.toolbar')).display),'none');
assert.deepEqual(errors,[]);assert.deepEqual(requests,[]);report.runs.push({reducedMotion,slides,testedNodes,errors,networkRequests:requests,clipping:0,keyboard:true,fullscreen:true,overview:true,reload:true,print:true});await context.close();
}
fs.writeFileSync(path.join(root,'evidence/test-report.json'),JSON.stringify(report,null,2)+'\n');console.log(JSON.stringify(report,null,2));
} finally {await browser.close();}