Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ jobs:
ln -s "$GITHUB_WORKSPACE/_site" /tmp/qe-preview/ai_qe
python -m http.server 61600 --bind 127.0.0.1 --directory /tmp/qe-preview > /tmp/qe-http.log 2>&1 &
node tools/check_audio_focus.cjs
node tools/architecture-demo/framing-test.cjs
node tools/architecture-demo/narration-test.cjs
node tools/fintech-browser-test.cjs
node tools/ai-adoption-browser-test.cjs
Expand Down
4 changes: 2 additions & 2 deletions _data/release.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
version: "1.21.1"
version: "1.21.2"
slide_edition: "1.12.2"
fintech_edition: "1.16.1"
updated: "2026-09-09"
latest_change: "Audio now hands off between open presentations, diagrams and embedded slides, preventing overlapping narration and stale automatic transitions."
latest_change: "The 3D platform has a higher, more balanced camera view, proportional framing at every screen size and a complete zoom-and-angle reset."
questionnaire_edition: "4"
research_edition: "1.7.0"
350 changes: 175 additions & 175 deletions assets/js/architecture-3d.js

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions releases.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ The version identifies the site and presentation player. A player-only patch can

<div class="hero-actions"><a class="btn btn-primary" href="https://github.com/tomqwu/ai_qe/releases">GitHub releases &amp; assets ↗</a><a class="text-link" href="{{ '/briefings/' | relative_url }}">Open the presentation room →</a></div>

## v1.21.2 · 9 September 2026

The [3D architecture]({{ '/demos/architecture/?scenario=generate' | relative_url }}) now opens from a **higher, more front-facing camera angle**, so the platform's depth is easier to read. Framing fits the model with space around its edges and preserves its proportions across wide, short and mobile windows. Camera motion keeps the same elevated view.

**Reset view** restores both the original angle and zoom, including after an orbit drag. The model, recordings, narration synchronization and original downloadable film retain their existing editions. [GitHub release v1.21.2](https://github.com/tomqwu/ai_qe/releases/tag/v1.21.2) packages the current publication.

## v1.21.1 · 9 September 2026

Starting audio now pauses other AI × QE players across **open tabs and windows on the same site**, page explanations and nested embedded presentations. Playback requests claim focus before loading finishes; a cancelled request cannot interrupt the newer player when its audio arrives.
Expand Down
43 changes: 43 additions & 0 deletions tools/architecture-demo/framing-test.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Validate the loaded model's projected bounds, not just CSS or camera constants.
const assert=require('node:assert/strict'),{chromium,webkit}=require('playwright');
const base=process.env.QE_TEST_URL||'http://127.0.0.1:61600/ai_qe';
async function framing(page){
await page.waitForFunction(()=>{const f=window.qeArchitecture?.framing;return f&&Math.abs(f.projectionAspect-f.width/f.height)<.0001});
return page.evaluate(()=>window.qeArchitecture.framing);
}
function checkFit(f){
assert.ok(Math.abs(f.projectionAspect-f.width/f.height)<.0001,'Equal horizontal and vertical projection scale');
assert.ok(f.groundDepth>.75,'The default view retains three quarters of the ground-plane depth instead of flattening it');
assert.ok(f.corners.every(([x,y])=>Math.abs(x)<.95&&Math.abs(y)<.95),'The complete model has clear margins: '+JSON.stringify(f));
}
(async()=>{for(const engine of [chromium,webkit]){
const browser=await engine.launch();try{
const page=await browser.newPage({viewport:{width:1440,height:1000},deviceScaleFactor:1.5}),errors=[];
page.on('pageerror',e=>errors.push(e.message));
await page.goto(base+'/demos/architecture/?scenario=generate');await page.waitForFunction(()=>window.qeArchitecture?.ready,{},{timeout:60000});
const home=await framing(page);checkFit(home);
for(const size of [{width:1920,height:1200},{width:1265,height:712},{width:1920,height:700},{width:900,height:1400},{width:390,height:844}]){
await page.setViewportSize(size);checkFit(await framing(page));
}
await page.setViewportSize({width:1440,height:1000});await framing(page);
const rect=await page.locator('#architecture-canvas').boundingBox();
await page.mouse.move(rect.x+rect.width*.5,rect.y+rect.height*.5);await page.mouse.wheel(0,-600);
await page.waitForFunction(()=>window.qeArchitecture.framing.zoom>1.2);
await page.mouse.down();await page.mouse.move(rect.x+rect.width*.6,rect.y+rect.height*.55,{steps:5});await page.mouse.up();
// Click while damping can still be active: reset must not drift away again.
await page.locator('[data-reset-camera]').click();await page.waitForTimeout(250);
const reset=await framing(page);assert.equal(reset.zoom,1);checkFit(reset);
assert.ok(reset.position.every((v,i)=>Math.abs(v-home.position[i])<.00001),'Reset restores the camera angle');
assert.ok(reset.target.every((v,i)=>Math.abs(v-home.target[i])<.00001),'Reset restores the center');
await page.waitForTimeout(250);assert.deepEqual((await framing(page)).position,reset.position,'No residual orbit drift');
for(const size of [{width:1265,height:712},{width:390,height:844}]){
await page.setViewportSize(size);await page.evaluate(()=>window.qeArchitecture.setCameraCinema(true));
for(const time of [0,7,14,21,28,35,42,48]){await page.evaluate(t=>window.qeArchitecture.seek(t),time);checkFit(await framing(page));}
await page.locator('[data-reset-camera]').click();checkFit(await framing(page));
assert.equal(await page.evaluate(()=>window.qeArchitecture.snapshot.cinematic),false);
}
await page.emulateMedia({reducedMotion:'reduce'});await page.locator('[data-reset-camera]').click();checkFit(await framing(page));
assert.deepEqual(errors,[]);
console.log(`${engine.name()}: proportional projection, complete model margins at five viewport shapes, elevated camera motion, zoom/orbit reset without drift, and reduced motion passed`);
}finally{await browser.close()}
}})().catch(e=>{console.error(e);process.exit(1)});
Loading
Loading