add transcript view ('r') for text-to-speech read-aloud extensions
Read-aloud browser extensions cannot walk a reveal.js deck: slides
outside the current one are display:none and the visible page text is
indexed only once at page load, so such extensions stop after the title
slide. Pressing 'r' now toggles a transcript view -- a single scrollable
page containing every slide's content and speaker notes (overview TOC
lists excluded, fragments shown, rebuilt on every open) -- which
extensions can read from start to finish. Styled in the ids theme;
closes on click or Escape.
Change-Id: Ia3df9dfa68b9699d318d747831ac8a0e270c8271
diff --git a/inst/reveal.js-5.2.1/dist/theme/ids.css b/inst/reveal.js-5.2.1/dist/theme/ids.css
index 16c8f5e..d1b8467 100644
--- a/inst/reveal.js-5.2.1/dist/theme/ids.css
+++ b/inst/reveal.js-5.2.1/dist/theme/ids.css
@@ -1091,4 +1091,51 @@
color: var(--r-heading-color);
}
+/*********************************************
+ * TRANSCRIPT / READ-ALOUD VIEW ('r')
+ *********************************************/
+#ids-transcript {
+ position: fixed;
+ inset: 0;
+ z-index: 90;
+ background: #fff;
+ color: #444444;
+ font-family: "Fira Sans", "Fira Sans Condensed", sans-serif;
+ font-size: 18px;
+ line-height: 1.5;
+ overflow-y: auto;
+ -webkit-overflow-scrolling: touch;
+ padding: max(32px, env(safe-area-inset-top)) max(40px, env(safe-area-inset-right))
+ max(32px, env(safe-area-inset-bottom)) max(40px, env(safe-area-inset-left));
+}
+#ids-transcript[hidden] {
+ display: none;
+}
+#ids-transcript .ids-transcript-slide {
+ max-width: 900px;
+ margin: 0 auto 2.5em auto;
+}
+#ids-transcript h1, #ids-transcript h2 {
+ color: var(--r-heading-color);
+ margin: 0 0 0.4em 0;
+}
+#ids-transcript h1 { font-size: 1.4em; }
+#ids-transcript h2 { font-size: 1.2em; }
+#ids-transcript .fragment {
+ opacity: 1;
+ visibility: visible;
+}
+#ids-transcript .ids-transcript-notes {
+ border-left: 3px solid #f6a800;
+ padding: 0.2em 0 0.2em 0.8em;
+ margin-top: 0.8em;
+ color: #666;
+}
+#ids-transcript .ids-transcript-notes:empty {
+ display: none;
+}
+#ids-transcript img, #ids-transcript video, #ids-transcript iframe {
+ max-width: 100%;
+}
+
/*# sourceMappingURL=ids.css.map */
diff --git a/inst/rmarkdown/templates/revealjs_presentation/resources/default.html b/inst/rmarkdown/templates/revealjs_presentation/resources/default.html
index 062ffa7..003336b 100644
--- a/inst/rmarkdown/templates/revealjs_presentation/resources/default.html
+++ b/inst/rmarkdown/templates/revealjs_presentation/resources/default.html
@@ -1045,5 +1045,74 @@
}
})();
</script>
+<script>
+ // Practice/read-aloud view: pressing 'r' toggles a single scrollable page
+ // containing every slide's content and speaker notes. Text-to-speech
+ // browser extensions cannot walk a reveal.js deck (hidden slides are
+ // display:none and indexed only once at page load), but they can read
+ // this flat transcript from start to finish. Styling in the ids theme.
+ (function () {
+ var overlay = null;
+
+ function build() {
+ overlay = document.createElement('div');
+ overlay.id = 'ids-transcript';
+ overlay.hidden = true;
+ overlay.setAttribute('aria-label', 'Transcript of all slides and notes');
+ Reveal.getSlides().forEach(function (slide) {
+ var section = document.createElement('section');
+ section.className = 'ids-transcript-slide';
+ var heading = slide.querySelector('h1,h2');
+ if (heading) section.appendChild(heading.cloneNode(true));
+ Array.prototype.forEach.call(slide.childNodes, function (node) {
+ if (node.nodeType !== Node.ELEMENT_NODE) return;
+ if (node.tagName === 'ASIDE' || node.tagName === 'H1' ||
+ node.tagName === 'H2' || node.tagName === 'SECTION' ||
+ (node.classList && node.classList.contains('ids-overview-toc'))) return;
+ section.appendChild(node.cloneNode(true));
+ });
+ var notes = Array.prototype.map.call(
+ slide.querySelectorAll('aside.notes'),
+ function (n) { return n.innerHTML; }
+ ).join('');
+ if (notes) {
+ var block = document.createElement('div');
+ block.className = 'ids-transcript-notes';
+ block.innerHTML = notes;
+ section.appendChild(block);
+ }
+ overlay.appendChild(section);
+ });
+ overlay.addEventListener('click', function () { overlay.hidden = true; });
+ document.body.appendChild(overlay);
+ }
+
+ function toggle() {
+ if (typeof Reveal === 'undefined' || !Reveal.isReady()) return;
+ if (!overlay) build();
+ overlay.hidden = !overlay.hidden;
+ if (!overlay.hidden) {
+ overlay.scrollTop = 0;
+ // rebuild on every open so the transcript reflects the current deck
+ overlay.remove();
+ overlay = null;
+ build();
+ overlay.hidden = false;
+ }
+ }
+
+ document.addEventListener('keydown', function (e) {
+ if (e.key !== 'r' && e.key !== 'R') return;
+ if (e.ctrlKey || e.altKey || e.metaKey) return;
+ var tag = e.target && e.target.tagName;
+ if (tag === 'INPUT' || tag === 'TEXTAREA' || (e.target && e.target.isContentEditable)) return;
+ e.preventDefault();
+ toggle();
+ });
+ document.addEventListener('keydown', function (e) {
+ if (e.key === 'Escape' && overlay && !overlay.hidden) overlay.hidden = true;
+ });
+ })();
+</script>
</body>
</html>