Refine presentation flow and speaker notes

This commit is contained in:
Codex
2026-09-24 22:28:58 +02:00
parent d5310d5a3f
commit 08f42a5f48
6 changed files with 162 additions and 276 deletions
+13 -10
View File
@@ -29,25 +29,28 @@ buttons and disappear entirely on slides without popups. The current preview omi
the 34px slide footer; the audience slide remains unchanged.
Arrow keys change slides; Escape closes
the popup. Changing slide closes any open popup. Notes scroll independently and
return to the beginning when the slide changes.
return to the beginning when the slide changes. The reading area contains only the
spoken narrative, without slide titles or popup headings. Parenthesized numbers
on slide 2 and slides 7–10 identify the popup button for each passage.
Screenshot-specific notes still follow the selected popup; use the controls below
the preview to open popups.
Popup controls, clickable images and speaker-note headings open only on click
Popup controls and clickable images open only on click
or explicit keyboard activation (Enter, Space or the numbered shortcuts).
Hover and keyboard focus may highlight a control but never open or switch a popup.
On slide 08,
the five numbered headings in the speaker notes also open the corresponding
Validation detail, centered on the slide with the shared lens effect. The fifth
On slide 07,
the five numbered controls open the corresponding Validation detail, centered on
the slide with the shared lens effect. The fifth
detail explains all three advantages; on the slide, the advantages panel highlights
on hover and opens on click. A detail
stays open when the pointer leaves, until another detail is selected, Escape or
Close is used, or the slide changes. The preview and audience show the same detail.
Slide 09 has seven numbered screenshot controls, in the supplied tour order.
Click a thumbnail, a console button or a numbered heading in its notes to open
the original screenshot almost full screen. Keys 1–7 also select screenshots;
Slide 08 has four numbered screenshot controls, in the supplied tour order.
Click a thumbnail or a console button to open
the original screenshot almost full screen. Keys 1–4 also select screenshots;
Escape or Close returns to the slide. Preview and audience share the selection.
The seven English descriptions form one short script (about 90 seconds including
brief pauses to change screens).
The four English descriptions form one continuous script.
The preview and audience communicate over a session-specific `BroadcastChannel`.
The session is in the console URL; the latest slide and popup are retained in local
+2 -2
View File
@@ -6,7 +6,7 @@
<title>AritmoLab | Presenter console</title>
<link rel="stylesheet" href="../deck/fonts.css">
<link rel="stylesheet" href="presenter.css?v=6">
<script src="presenter.js?v=22" defer></script>
<script src="presenter.js?v=23" defer></script>
</head>
<body>
<header class="toolbar">
@@ -33,7 +33,7 @@
</section>
<section class="script" aria-labelledby="notes-heading">
<div class="section-heading"><h2 id="notes-heading">Speaker notes</h2><div class="reading-controls"><button id="smaller" aria-label="Smaller notes">A−</button><button id="larger" aria-label="Larger notes">A+</button><label class="sr-only" for="layout">Notes layout</label><select id="layout"><option value="side">Notes beside preview</option><option value="below">Notes below preview</option></select></div></div>
<div id="reading" tabindex="0" aria-label="Scrollable speaker notes"><div class="slide-label" id="notes-number">SLIDE 01</div><h1 id="notes-title">Preparing your presentation</h1><article id="notes">The slide and its speaker notes will appear here.</article></div>
<div id="reading" tabindex="0" aria-label="Scrollable speaker notes"><article id="notes">The slide and its speaker notes will appear here.</article></div>
<footer><span>Notes are visible only in this window.</span><span><kbd>←</kbd> <kbd>→</kbd> Change slide · <kbd>Esc</kbd> Close popup</span></footer>
</section>
</main>
+2 -17
View File
@@ -19,20 +19,8 @@
let lastPreviewSeen = 0;
let notesSize = 25;
let blocked = false;
const urlFor = role => `../deck/index.html?v=18&view=${role}&session=${encodeURIComponent(session)}`;
const urlFor = role => `../deck/index.html?v=19&view=${role}&session=${encodeURIComponent(session)}`;
const pad = value => String(value).padStart(2, '0');
function showNoteLens(event) {
const cue = event.target.closest('[data-note-lens]');
if (!cue || !slides[current?.index]?.popups.some(popup => popup.kind === 'lens' && popup.key === cue.dataset.noteLens)) return;
if (current?.popup?.key !== cue.dataset.noteLens) command('popup', { popup: { kind: 'lens', key: cue.dataset.noteLens } });
}
$('notes').addEventListener('click', showNoteLens);
$('notes').addEventListener('click', event => {
const cue = event.target.closest('[data-note-screenshot]');
if (cue && slides[current?.index]?.popups.some(popup => popup.kind === 'screenshot' && popup.key === cue.dataset.noteScreenshot)) {
command('popup', { popup: { kind: 'screenshot', key: cue.dataset.noteScreenshot } });
}
});
function renderNotes(html) {
const template = document.createElement('template');
template.innerHTML = html;
@@ -99,8 +87,6 @@
$('next').disabled = state.index === slides.length - 1;
$('close-popup').disabled = !state.popup;
if (changed) {
$('notes-number').textContent = `SLIDE ${pad(state.index + 1)}`;
$('notes-title').textContent = slide.title;
// Trusted, same-origin authored notes, never channel-provided HTML.
if (!slide.popupNotes?.length) renderNotes(slide.notes);
currentNoteKey = null;
@@ -157,7 +143,6 @@
|| slide.popupNotes[0];
if (note.key !== currentNoteKey) {
currentNoteKey = note.key;
$('notes-title').textContent = note.title;
renderNotes(note.html);
$('reading').scrollTop = 0;
}
@@ -208,7 +193,7 @@
updateNextPreview();
});
// No session token: the thumbnail must never navigate or publish to the audience.
nextPreview.src = '../deck/index.html?v=18&view=thumbnail#/1';
nextPreview.src = '../deck/index.html?v=19&view=thumbnail#/1';
const stage = document.querySelector('.stage');
function sizeCurrentPreview() {
const style = getComputedStyle(stage);