tahamajs/RR / Docs /visualizer /src /educational /present_mode.js
tahamajs's picture
download
raw
5.3 kB
/**
* educational/present_mode.js
* ===========================
* Fullscreen Cinematic Presentation Mode (5-Minute Demo Ready).
* Minimal interface with:
* - Semi-transparent glassmorphic inspector drawer
* - 7-Stage Token Progress Bar at the bottom
* - Smooth camera transitions and slower, high-elegance particle physics
* - Escape key to exit seamlessly
*/
import { ATTENTION_TOUR_STEPS } from './attention_tour.js';
let isPresenting = false;
let presentStep = 0;
let presentTimer = null;
/**
* Toggles Presentation Mode on and off.
* @param {object} context - { camera, controls, store, rebuildSceneForModel }
*/
export function togglePresentMode(context = {}) {
isPresenting = !isPresenting;
const body = document.body;
let bar = document.getElementById('present-dock-bar');
if (!bar) {
bar = document.createElement('div');
bar.id = 'present-dock-bar';
bar.className = 'present-dock-bar hidden';
bar.innerHTML = `
<div class="present-banner" style="position:fixed; top:18px; left:50%; transform:translateX(-50%); z-index:9999; background:rgba(6,13,24,0.85); backdrop-filter:blur(16px); border:1px solid rgba(133,198,235,0.3); border-radius:30px; padding:8px 24px; display:flex; align-items:center; gap:16px; box-shadow:0 12px 40px rgba(0,0,0,0.6);">
<span class="badge" style="background:rgba(16,185,129,0.2); color:#34d399; font-weight:700;">▶ 5-MIN DEMO PRESENTATION</span>
<span id="present-step-title" style="font-size:13px; font-weight:600; color:#eef8ff;">Welcome to Attention Lab</span>
<div style="display:flex; gap:8px;">
<button id="btn-present-prev" class="round-button" style="padding:2px 10px; font-size:11px;">◀ Prev</button>
<button id="btn-present-next" class="round-button" style="padding:2px 10px; font-size:11px; background:rgba(34,213,247,0.2); color:#38bdf8;">Next ▶</button>
<button id="btn-present-exit" class="round-button" style="padding:2px 10px; font-size:11px; color:#f87171;">✕ Exit (Esc)</button>
</div>
</div>
<div class="present-progress-track" style="position:fixed; bottom:20px; left:50%; transform:translateX(-50%); z-index:9999; background:rgba(6,13,24,0.85); backdrop-filter:blur(16px); border:1px solid rgba(133,198,235,0.25); border-radius:24px; padding:10px 24px; display:flex; gap:12px; align-items:center;">
${ATTENTION_TOUR_STEPS.map((s, i) => `
<div class="present-dot ${i === 0 ? 'active' : ''}" data-step="${i}" style="width:12px; height:12px; border-radius:50%; background:${i === 0 ? 'var(--cyan)' : 'rgba(255,255,255,0.2)'}; cursor:pointer; transition:all 0.3s;" title="${s.title}"></div>
`).join('')}
</div>
`;
document.body.appendChild(bar);
bar.querySelector('#btn-present-prev').addEventListener('click', () => stepPresent(-1, context));
bar.querySelector('#btn-present-next').addEventListener('click', () => stepPresent(1, context));
bar.querySelector('#btn-present-exit').addEventListener('click', () => togglePresentMode(context));
bar.querySelectorAll('.present-dot').forEach(dot => {
dot.addEventListener('click', () => {
presentStep = parseInt(dot.dataset.step, 10);
showStep(presentStep, context);
});
});
}
if (isPresenting) {
body.classList.add('present-mode');
bar.classList.remove('hidden');
presentStep = 0;
showStep(presentStep, context);
try {
if (document.documentElement.requestFullscreen) {
document.documentElement.requestFullscreen().catch(() => {});
}
} catch (e) {}
} else {
body.classList.remove('present-mode');
bar.classList.add('hidden');
clearTimeout(presentTimer);
try {
if (document.exitFullscreen && document.fullscreenElement) {
document.exitFullscreen().catch(() => {});
}
} catch (e) {}
}
}
function stepPresent(delta, context) {
presentStep = (presentStep + delta + ATTENTION_TOUR_STEPS.length) % ATTENTION_TOUR_STEPS.length;
showStep(presentStep, context);
}
function showStep(stepIdx, context) {
const s = ATTENTION_TOUR_STEPS[stepIdx];
if (!s) return;
const titleEl = document.getElementById('present-step-title');
if (titleEl) titleEl.innerText = `${s.step}/10 · ${s.title}`;
// Update Dots
document.querySelectorAll('.present-dot').forEach((dot, i) => {
dot.style.background = (i === stepIdx) ? 'var(--cyan)' : 'rgba(255,255,255,0.2)';
dot.style.transform = (i === stepIdx) ? 'scale(1.4)' : 'scale(1.0)';
});
// Fly Camera
if (context.camera && context.controls && window.smoothFlyCamera) {
window.smoothFlyCamera(s.y, s.dist);
}
// Select Part in 3D
if (s.target && window.selectPartIn3D) {
window.selectPartIn3D(s.target);
}
}
// Escape key listener to exit Presentation Mode
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && isPresenting) {
togglePresentMode();
}
});

Xet Storage Details

Size:
5.3 kB
·
Xet hash:
2f82de2c2fd56739798c7baea61b0017cb42d197ccf468f1cee47bb4ed637458

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.