Merge pull request 'feat: playable homepage presentation under Understand In Speed' (#42) from feat/homepage-presentation-embed into main
Build and publish Eldorado documentation / build (push) Successful in 52s

This commit was merged in pull request #42.
This commit is contained in:
jarvis-at-skic committed 2026-10-08 11:41:35 -07:00
commit eea14bf00f
10 files changed
+171 -10

No files matched your search

+1
View File
@@ -12,6 +12,7 @@
"presentations:build": "node scripts/build-presentations.mjs",
"serve": "docusaurus serve",
"test:presentation": "node scripts/browser/presentation.cjs",
"test:homepage-embed": "node scripts/browser/homepage-embed.cjs",
"typecheck": "tsc --noEmit",
"verify": "pnpm test && node scripts/verify.mjs",
"verify:build": "node scripts/verify.mjs --build",
@@ -2,6 +2,13 @@
const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
const portrait = window.matchMedia('(max-width: 700px) and (orientation: portrait)');
const printing = new URLSearchParams(location.search).has('print-pdf');
const embedded = new URLSearchParams(location.search).has('embed') && !printing;
document.documentElement.classList.toggle('embedded', embedded);
if (embedded) {
document.querySelector('.toolbar').hidden = true;
// Never load the portal inside the stage when following the reference appendix.
document.querySelectorAll('.slides a').forEach(link => { link.target = '_top'; });
}
function layoutSize(){
const narrow=portrait.matches&&!printing;
document.documentElement.classList.toggle('portrait',narrow);
@@ -9,10 +16,13 @@ function layoutSize(){
}
Reveal.initialize({
...layoutSize(), margin:0.04, minScale:0.1, maxScale:2,
hash:true, hashOneBasedIndex:false, controls:true, progress:true,
hash:!embedded, embedded, keyboardCondition:embedded ? () => document.hasFocus() : null,
hashOneBasedIndex:false, controls:true, progress:true,
slideNumber:'c/t', showSlideNumber:'all', center:false,
transition:reducedMotion.matches?'none':'fade', transitionSpeed:'fast',
backgroundTransition:'none', autoSlide:0, touch:true, keyboard:true,
backgroundTransition:'none', autoSlide:0, touch:true,
// The host owns fullscreen; speaker tools remain available in the standalone deck.
keyboard:embedded ? {70:null,83:null} : true,
// Keep deliberate slides on phones rather than Reveal's automatic scroll view.
scrollActivationWidth:null, pdfMaxPagesPerSlide:1, pdfSeparateFragments:false,
plugins:[RevealNotes]
@@ -55,5 +55,12 @@ a:focus-visible,button:focus-visible{outline:3px solid #b88719;outline-offset:3p
.portrait #qualification .contrast{grid-template-columns:1fr 1fr;gap:24px}.portrait #qualification .contrast p{font-size:24px}
.portrait .deck .references{font-size:29px}.portrait .deck .references li{margin:22px 0}
@media(prefers-reduced-motion:reduce){*{transition:none!important;animation:none!important}}
.embedded .toolbar{display:none}
.embedded .reveal.deck{top:0;height:calc(100dvh - 48px)}
.embedded body{background:#101014}
.embedded .deck .controls{position:fixed;bottom:8px;color:var(--gold)!important}
.embedded .deck .progress{color:var(--gold)}
.embedded .deck .slide-number{position:fixed;bottom:12px}
.embedded .deck .progress{position:fixed;bottom:0}
@media print{.toolbar{display:none}.reveal.deck{top:0;height:100%}}
html.print-pdf .toolbar{display:none}html.print-pdf .reveal.deck{top:0;height:100%}
+57
View File
@@ -0,0 +1,57 @@
const {chromium}=require('playwright');
const assert=require('node:assert/strict');const fs=require('node:fs');
(async()=>{
const base=process.argv[2]||'http://127.0.0.1:43853';const root=base+'/eldorado-v1/documentation/';
const out='.cache/homepage-embed';fs.mkdirSync(out,{recursive:true});
const browser=await chromium.launch({headless:true,executablePath:process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE,args:['--no-sandbox']});const results=[];
try{
for(const viewport of [{width:1440,height:900},{width:390,height:844},{width:320,height:720},{width:844,height:390}]){
const context=await browser.newContext({viewport,hasTouch:true,reducedMotion:'reduce'});const p=await context.newPage();const errors=[];
p.on('pageerror',e=>errors.push(e.message));p.on('response',r=>{if(r.status()>=400)errors.push(`${r.status()} ${r.url()}`)});
await p.goto(root,{waitUntil:'networkidle'});const section=p.locator('section[aria-labelledby="presentation-title"]');await section.scrollIntoViewIfNeeded();
const iframe=p.locator('iframe[title="Less Prompting, More Shipping — interactive presentation"]');const f=await iframe.elementHandle().then(e=>e.contentFrame());await f.waitForFunction(()=>window.Reveal?.isReady());await f.evaluate(()=>document.fonts.ready);
assert.deepEqual(await p.locator('#start-title,#presentation-title').evaluateAll(es=>es.map(e=>{const s=getComputedStyle(e);return [s.fontFamily,s.fontSize,s.fontWeight,s.letterSpacing,s.lineHeight]})).then(a=>a[0]),await p.locator('#presentation-title').evaluate(e=>{const s=getComputedStyle(e);return [s.fontFamily,s.fontSize,s.fontWeight,s.letterSpacing,s.lineHeight]}));
assert.equal(await f.locator('.toolbar').isVisible(),false);assert.equal(await f.evaluate(()=>Reveal.getConfig().hash),false);assert.equal(await f.evaluate(()=>Reveal.getConfig().transition),'none');assert.equal(await f.evaluate(()=>Reveal.getTotalSlides()),19);
assert(await p.evaluate(()=>document.documentElement.scrollWidth<=innerWidth));
if(viewport.width<=390)assert(await iframe.evaluate(e=>e.clientHeight>e.clientWidth),'phone stage must recompose rather than shrink landscape text');
await p.getByRole('button',{name:'Fullscreen',exact:true}).focus();await p.keyboard.press('ArrowRight');assert.equal(await f.evaluate(()=>Reveal.getIndices().h),0);
await f.locator('.controls .navigate-right').click();assert.equal(await f.evaluate(()=>Reveal.getIndices().h),1);
const scroll=await p.evaluate(()=>scrollY);await p.keyboard.press('ArrowRight');assert.equal(await f.evaluate(()=>Reveal.getIndices().h),2);assert.equal(await p.evaluate(()=>scrollY),scroll);assert.equal(p.url(),root);
await f.evaluate(()=>Reveal.slide(0));
const visited=[];
for(let i=0;i<19;i++){
assert.equal(await f.evaluate(()=>Reveal.getIndices().h),i);visited.push(await f.evaluate(()=>Reveal.getCurrentSlide().id));
const bad=await f.evaluate(()=>{const r=Reveal.getCurrentSlide().getBoundingClientRect();return [...Reveal.getCurrentSlide().querySelectorAll('h1,h2,p,li,table,.flow,.roles,.timeline')].filter(e=>!e.closest('.notes')).filter(e=>{const b=e.getBoundingClientRect();return b.bottom>r.bottom+1||b.right>r.right+1||b.left<r.left-1}).map(e=>e.textContent)});assert.deepEqual(bad,[]);
await iframe.screenshot({path:`${out}/${viewport.width}-${i+1}.png`});
if(i<18)await p.keyboard.press('ArrowRight');
}
assert.equal(new Set(visited).size,19);assert.equal(p.url(),root);assert.equal(new URL(f.url()).hash,'');
await f.evaluate(()=>Reveal.slide(0));await section.screenshot({path:`${out}/${viewport.width}-light.png`});
await p.evaluate(()=>document.documentElement.setAttribute('data-theme','dark'));await section.screenshot({path:`${out}/${viewport.width}-dark.png`});
await p.getByRole('button',{name:'Fullscreen',exact:true}).click();await p.waitForFunction(()=>!!document.fullscreenElement);await f.waitForTimeout(100);
await p.screenshot({path:`${out}/${viewport.width}-fullscreen.png`});
await p.getByRole('button',{name:'Exit fullscreen',exact:true}).click();await p.waitForFunction(()=>!document.fullscreenElement);await p.waitForFunction(()=>document.activeElement?.textContent==='Fullscreen');
// Native touch swipe inside the stage, not a programmatic Reveal.next().
await iframe.scrollIntoViewIfNeeded();const b=await iframe.boundingBox();const cdp=await context.newCDPSession(p);
await cdp.send('Input.dispatchTouchEvent',{type:'touchStart',touchPoints:[{x:b.x+b.width*.8,y:b.y+b.height*.4}]});
await cdp.send('Input.dispatchTouchEvent',{type:'touchMove',touchPoints:[{x:b.x+b.width*.2,y:b.y+b.height*.4}]});await cdp.send('Input.dispatchTouchEvent',{type:'touchEnd',touchPoints:[]});assert.equal(await f.evaluate(()=>Reveal.getIndices().h),1);
await p.emulateMedia({reducedMotion:'no-preference'});await f.waitForFunction(()=>Reveal.getConfig().transition==='fade');await p.emulateMedia({reducedMotion:'reduce'});await f.waitForFunction(()=>Reveal.getConfig().transition==='none');
await f.locator('.navigate-right').focus();await p.keyboard.press('f');await p.keyboard.press('s');assert.equal(context.pages().length,1);assert.equal(await p.evaluate(()=>!!document.fullscreenElement),false);
if(viewport.width<=390){
await p.setViewportSize({width:844,height:390});await f.waitForFunction(()=>Reveal.getConfig().width===1200);assert.equal(await f.evaluate(()=>Reveal.getIndices().h),1);
await p.setViewportSize(viewport);await f.waitForFunction(()=>Reveal.getConfig().width===600);
}
// Unsupported fullscreen is non-destructive and keeps a working standalone escape.
await p.evaluate(()=>{window.savedFullscreen=Element.prototype.requestFullscreen;Element.prototype.requestFullscreen=undefined});
await p.getByRole('button',{name:'Fullscreen',exact:true}).click();await p.getByRole('status').filter({hasText:'Fullscreen is unavailable'}).waitFor();assert.equal(p.url(),root);
await p.evaluate(()=>{Element.prototype.requestFullscreen=window.savedFullscreen});
// No keyboard trap: browser Tab can leave the frame for the standalone affordance.
await f.locator('.navigate-right').focus();for(let i=0;i<12&&!(await p.getByRole('link',{name:/Open presentation/}).evaluate(e=>e===document.activeElement));i++)await p.keyboard.press('Tab');
assert(await p.getByRole('link',{name:/Open presentation/}).evaluate(e=>e===document.activeElement));
for(const href of await p.locator('.start-card').evaluateAll(es=>es.map(e=>e.href)))assert.equal((await p.request.get(href)).status(),200);
await p.getByRole('link',{name:/Open presentation/}).click();await p.waitForFunction(()=>window.Reveal?.isReady());assert(!p.url().includes('embed'));assert(await p.locator('.toolbar').isVisible());
assert.deepEqual(errors,[]);results.push({viewport,visited,headingParity:true,keyboardIsolation:true,scrollIsolation:true,touch:true,fullscreen:true,tabExit:true,errors});await context.close();
}
fs.writeFileSync(`${out}/results.json`,JSON.stringify(results,null,2));console.log('PASS: 76 inline slides, heading parity, URLs, focus/scroll isolation, touch, fullscreen, Tab exit, themes, reduced motion and standalone handoff');
}finally{await browser.close()}
})().catch(e=>{console.error(e);process.exitCode=1});
+37
View File
@@ -0,0 +1,37 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import {readFileSync} from 'node:fs';
import vm from 'node:vm';
function runtime(search) {
let config; const classes=new Set(); const links=[{}];
const document={documentElement:{classList:{toggle:(name,on)=>on?classes.add(name):classes.delete(name)}},getElementById:()=>({addEventListener(){},hidden:false}),querySelector:()=>({hidden:false}),querySelectorAll:()=>links,hasFocus:()=>true};
const window={matchMedia:()=>({matches:false,addEventListener(){}})};
vm.runInNewContext(read('presentations/less-prompting-more-shipping/runtime.js'),{window,document,location:{search},URLSearchParams,RevealNotes:{},Reveal:{initialize:c=>{config=c;},configure(){}}});
return {config,classes,links};
}
test('embed isolates URL, focus and deck links without autoplay; print stays standalone',()=>{
const embed=runtime('?embed');
assert.equal(embed.config.hash,false);
assert.equal(embed.config.embedded,true);
assert.equal(embed.config.autoSlide,0);
assert.equal(embed.config.keyboard[70],null);
assert.equal(embed.config.keyboard[83],null);
assert.equal(typeof embed.config.keyboardCondition,'function');
assert(embed.classes.has('embedded'));
assert.equal(embed.links[0].target,'_top');
for(const query of ['', '?print-pdf', '?embed&print-pdf']) {
const {config,classes}=runtime(query);
assert.equal(config.hash,true);assert(!classes.has('embedded'));
}
});
const read=p=>readFileSync(p,'utf8');
test('homepage replaces teaser with titled single-source playable stage below existing cards',()=>{
const home=read('src/pages/index.tsx');
assert.match(home,/<div className="section-heading"><div><h2 id="presentation-title">Understand In Speed<\/h2>/);
assert(home.indexOf('Understand In Speed')>home.indexOf('Shaping the direction.'));
assert.match(home,/<PresentationEmbed/);
assert(!home.includes('35-minute presentation'));
const component=read('src/components/PresentationEmbed/index.tsx');
assert.match(component,/<iframe/);assert.match(component,/title=/);assert.match(component,/\?embed/);
assert.match(component,/allowFullScreen/);
});
+2 -2
View File
@@ -22,9 +22,9 @@ test('build emits local pinned Reveal assets, stable IDs and no production CDN',
assert(!/https?:\/\//.test(html));
assert.match(html,/href="\.\.\/\.\.\/strategy\/target-operating-model\/"/);
assert.match(read(`${dir}/assets/reveal.js`),/5\.2\.1/);
assert.match(read(`${dir}/runtime.js`),/hash:true/);
assert.match(read(`${dir}/runtime.js`),/hash:!embedded/);
assert.equal(JSON.parse(read('package.json')).dependencies['reveal.js'],'5.2.1');
assert.match(read('src/pages/index.tsx'),/Open presentation/);
assert.match(read('src/components/PresentationEmbed/index.tsx'),/Open presentation/);
const map=JSON.parse(read(`${dir}/source-map.json`));
assert.equal(map.version,'1.0.1');
assert.match(map.strategyRevision,/same-build/);
+1 -1
View File
@@ -91,7 +91,7 @@ export async function verifySidebarCoverage() {
const body = readFileSync(join('build',file),'utf8');
assert.equal(body, readFileSync(join('static',file),'utf8'));
assert(body.includes('class="reveal deck"') && body.includes('class="notes"'));
assert(readFileSync('build/index.html','utf8').includes('./presentations/less-prompting-more-shipping/'));
assert(readFileSync('build/index.html','utf8').includes('/presentations/less-prompting-more-shipping/?embed'));
reason = 'Reveal presentation linked from the homepage, not a competing documentation page';
} else if (file.startsWith('diagrams/') && readFileSync(join('build',file),'utf8').includes('<svg')) {
assert.equal(readFileSync(join('build',file),'utf8'), readFileSync(join('static',file),'utf8'), `Viewer must match generated static asset: ${file}`);
@@ -0,0 +1,39 @@
import React, {useEffect, useRef, useState} from 'react';
import useBaseUrl from '@docusaurus/useBaseUrl';
import styles from './styles.module.css';
/** One canonical deck document, with chrome and URL ownership isolated from the portal. */
export default function PresentationEmbed({title, path}: {title: string; path: string}): React.ReactElement {
const url = useBaseUrl(path);
const stage = useRef<HTMLDivElement>(null);
const button = useRef<HTMLButtonElement>(null);
const [fullscreen, setFullscreen] = useState(false);
const [error, setError] = useState('');
useEffect(() => {
const changed = () => {
const active = document.fullscreenElement === stage.current;
setFullscreen(active);
if (!active) button.current?.focus({preventScroll: true});
};
document.addEventListener('fullscreenchange', changed);
return () => document.removeEventListener('fullscreenchange', changed);
}, []);
async function toggleFullscreen() {
setError('');
try {
if (document.fullscreenElement === stage.current) await document.exitFullscreen();
else if (stage.current?.requestFullscreen) await stage.current.requestFullscreen();
else setError('Fullscreen is unavailable. Open presentation for a larger view.');
} catch {
setError('Fullscreen is unavailable. Open presentation for a larger view.');
}
}
return <div className={styles.stage} ref={stage}>
<iframe className={styles.frame} src={`${url}?embed`} title={`${title} — interactive presentation`} loading="lazy" allowFullScreen/>
<div className={styles.actions}>
<a href={url}>Open presentation <span aria-hidden="true">↗</span></a>
<button ref={button} type="button" onClick={toggleFullscreen} aria-pressed={fullscreen}>{fullscreen ? 'Exit fullscreen' : 'Fullscreen'}</button>
</div>
{error && <p role="status" className={styles.error}>{error}</p>}
</div>;
}
@@ -0,0 +1,12 @@
.stage {border: 1px solid var(--ifm-color-emphasis-300); background: #1f1f29;}
.frame {display: block; width: 100%; aspect-ratio: 1200 / 720; border: 0; background: #1f1f29;}
.actions {display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 16px; background: #101014; color: #fff; font-size: .875rem;}
.actions a {color: #ffc950;}
.actions button {font: inherit; color: #fff; background: transparent; border: 1px solid #62616b; padding: 8px 12px; cursor: pointer;}
.actions :focus-visible, .frame:focus-visible {outline: 3px solid #ffc950; outline-offset: -3px;}
.error {padding: 12px; margin: 0; color: #fff;}
/* Reuse the deck's portrait composition on phones; don't shrink its landscape text. */
@media (max-width: 600px) {.frame {aspect-ratio: 600 / 1100;}}
.stage:fullscreen {width: 100%; height: 100%; display: flex; flex-direction: column; border: 0;}
.stage:fullscreen .frame {flex: 1; min-height: 0; aspect-ratio: auto;}
.stage:fullscreen .actions {flex: none;}
+3 -5
View File
@@ -1,6 +1,7 @@
import React from 'react';
import Layout from '@theme/Layout';
import Link from '@docusaurus/Link';
import PresentationEmbed from '../components/PresentationEmbed';
export default function Home(): React.ReactElement {
return <Layout title="Eldorado Powered By AI" description="Strategy and direction for an AI-powered Eldorado.">
@@ -31,11 +32,8 @@ export default function Home(): React.ReactElement {
<aside className="scope-note"><span className="scope-symbol" aria-hidden="true">✦</span><div><strong>Shaping the direction.</strong><p>This space is dedicated to AI strategy and direction for Eldorado. Specific initiatives, priorities, and the roadmap remain to be defined.</p></div></aside>
</section>
<section className="start-section container" aria-labelledby="presentation-title">
<p className="eyebrow">THE AI ARCHITECT’S FIRST SIX MONTHS</p>
<h2 id="presentation-title">Less Prompting, More Shipping</h2>
<p>A practical conversation about improving accepted engineering work: one willing team, a bounded experiment, honest measurement and an evidence-led next step.</p>
<p>35-minute presentation · 15-minute discussion · proposed strategy, not company results.</p>
<a className="button button--primary" href="./presentations/less-prompting-more-shipping/">Open presentation <span aria-hidden="true">↗</span></a>
<div className="section-heading"><div><h2 id="presentation-title">Understand In Speed</h2></div></div>
<PresentationEmbed title="Less Prompting, More Shipping" path="/presentations/less-prompting-more-shipping/"/>
</section>
</main>
</Layout>;