overview TOC: only chapter headings by default, two columns with sub-headings

The overview table of contents now lists only top-level (#) headings by
default. Adding {data-overview-subheadings=true} to the overview
heading opts in to the ## sub-headings below their chapters, which are
then laid out in two CSS columns (chapters kept intact across column
breaks).

Change-Id: Ic916c07b5479e8d19fd942e9151b0a471bf039a4
diff --git a/NEWS.md b/NEWS.md
index ac0533a..829cb18 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,6 +1,6 @@
 # revealjs.ids (development version)
 
-- Slides titled "Overview" or "Überblick" are now automatically turned into a table of contents (ported from the old Google Docs slide workflow): their body content is replaced at presentation time with a hierarchical, clickable list of all `#`/`##` slide headings.
+- Slides titled "Overview" or "Überblick" are now automatically turned into a table of contents (ported from the old Google Docs slide workflow): their body content is replaced at presentation time with a clickable list of all `#` chapter headings. Adding `{data-overview-subheadings=true}` to the heading also includes the `##` sub-headings, laid out in two columns.
 
 - Speaker notes (`::: notes`) and timing comments placed before the first heading are now attached to the title slide instead of ending up on a separate empty phantom slide after it, so the title itself can have a time budget and a welcome note (shown in the speaker view and the mobile notes overlay, and included in `slide_timing()` and the pacing timer).
 
diff --git a/README.Rmd b/README.Rmd
index 3a34e5a..2ee1375 100644
--- a/README.Rmd
+++ b/README.Rmd
@@ -156,7 +156,7 @@
 
 ## Overview Slides
 
-Like in the old Google-Docs-based slide workflow, a slide titled "Overview" or "Überblick" is automatically turned into a table of contents: its body content is replaced with a hierarchical list of all `#` and `##` slide headings, and each entry links to the corresponding slide.
+Like in the old Google-Docs-based slide workflow, a slide titled "Overview" or "Überblick" is automatically turned into a table of contents: its body content is replaced with a list of all `#` chapter headings, and each entry links to the corresponding slide.
 
 ``` markdown
 ## Overview
@@ -164,6 +164,12 @@
 (any placeholder content -- replaced automatically)
 ```
 
+To also include the `##` sub-headings below their chapters, add `{data-overview-subheadings=true}` to the heading; the list is then laid out in two columns:
+
+``` markdown
+## Overview {data-overview-subheadings=true}
+```
+
 The TOC is generated when the presentation is opened, so it always reflects the deck it is part of. Slides without a heading and the title slide are left out.
 
 ## Incremental Bullets
diff --git a/README.md b/README.md
index e7b2515..a993038 100644
--- a/README.md
+++ b/README.md
@@ -199,9 +199,8 @@
 
 Like in the old Google-Docs-based slide workflow, a slide titled
 "Overview" or "Überblick" is automatically turned into a table of
-contents: its body content is replaced with a hierarchical list of all
-`#` and `##` slide headings, and each entry links to the corresponding
-slide.
+contents: its body content is replaced with a list of all `#` chapter
+headings, and each entry links to the corresponding slide.
 
 ``` markdown
 ## Overview
@@ -209,6 +208,14 @@
 (any placeholder content -- replaced automatically)
 ```
 
+To also include the `##` sub-headings below their chapters, add
+`{data-overview-subheadings=true}` to the heading; the list is then
+laid out in two columns:
+
+``` markdown
+## Overview {data-overview-subheadings=true}
+```
+
 The TOC is generated when the presentation is opened, so it always
 reflects the deck it is part of. Slides without a heading and the
 title slide are left out.
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 5ae8600..16c8f5e 100644
--- a/inst/reveal.js-5.2.1/dist/theme/ids.css
+++ b/inst/reveal.js-5.2.1/dist/theme/ids.css
@@ -1074,6 +1074,13 @@
   padding: 0 0 0 1.1em;
   font-weight: normal;
 }
+.reveal .ids-overview-toc.with-subheadings {
+  columns: 2;
+  column-gap: 2.5em;
+}
+.reveal .ids-overview-toc.with-subheadings > li {
+  break-inside: avoid;
+}
 .reveal .ids-overview-toc ul li {
   margin: 0.18em 0;
 }
diff --git a/inst/rmarkdown/templates/revealjs_presentation/resources/default.html b/inst/rmarkdown/templates/revealjs_presentation/resources/default.html
index a557cf6..9bda0eb 100644
--- a/inst/rmarkdown/templates/revealjs_presentation/resources/default.html
+++ b/inst/rmarkdown/templates/revealjs_presentation/resources/default.html
@@ -978,9 +978,11 @@
 $endif$
 <script>
   // Slides whose (only) heading is "Overview" or "Überblick" are
-  // automatically populated with a table of contents built from the deck's
-  // h1/h2 slide headings, replacing whatever body content the slide had
-  // (ported from the Google Docs slide workflow). Styling in the ids theme.
+  // automatically populated with a table of contents of the deck's h1 slide
+  // headings, replacing whatever body content the slide had (ported from the
+  // Google Docs slide workflow). By adding {data-overview-subheadings=true}
+  // to the heading, h2 sub-headings are included below their chapters and
+  // the list is laid out in two columns. Styling in the ids theme.
   (function () {
     var TITLES = ['overview', 'überblick'];
 
@@ -998,13 +1000,17 @@
         if (!isOverviewSlide(target) || target.dataset.tocDone) return;
         target.dataset.tocDone = '1';
         var heading = target.querySelector('h1,h2');
+        var includeSub = (heading.getAttribute('data-overview-subheadings') || '')
+          .toLowerCase() !== 'false'
+          && heading.hasAttribute('data-overview-subheadings');
         var list = document.createElement('ul');
         list.className = 'ids-overview-toc';
+        if (includeSub) list.classList.add('with-subheadings');
         var subList = null;
         slides.forEach(function (slide) {
           if (slide === target || !slide.id || isOverviewSlide(slide)) return;
           var h = slide.querySelector('h1,h2');
-          if (!h) return;
+          if (!h || (h.tagName !== 'H1' && !includeSub)) return;
           var link = document.createElement('a');
           link.href = '#/' + slide.id;
           link.textContent = h.textContent;
diff --git a/man/revealjs_presentation.Rd b/man/revealjs_presentation.Rd
index d9fefed..62c1182 100644
--- a/man/revealjs_presentation.Rd
+++ b/man/revealjs_presentation.Rd
@@ -233,7 +233,9 @@
 Like in the original Google Docs slide workflow, a slide titled
 "Overview" or "Überblick" is automatically turned into a table of
 contents: its body content is replaced at presentation time with a
-hierarchical, clickable list of all \verb{#}/\verb{##} slide headings.
+clickable list of all \verb{#} chapter headings. Adding
+\code{{data-overview-subheadings=true}} to the heading also includes the
+\verb{##} sub-headings below their chapters, laid out in two columns.
 \subsection{Search}{
 
 When opt-in, it is possible to show a search box when pressing \code{CTRL + SHIFT + F}. It will seach in the whole presentation, and highlight matched words. The