ids theme: automatic chapter numbering, ported from gdoc2reveal

Chapter headings (#) are numbered automatically when the presentation is
opened: "1. Introduction", "2. Methods", and so on. References, appendix
and thank-you chapters keep their bare titles and are not counted.
Manually typed leading numbers ("# 2. Methods") are stripped before
renumbering, so decks migrated from the old Google Docs pipeline and
repeated numbering runs stay stable.

The menu plugin and the transcript view show the numbers, too; the
Overview slide's table of contents prepends them as orange
.ids-toc-number spans flush with the slide heading, with the theme's
drawn list markers suppressed for the top-level entries (sub-headings
keep their bullets). Decks rendered with slide_level: 1 are left
unnumbered -- there, headings denote slides, not chapters.

Bump version to 0.9.1.9010.

Change-Id: I233086e039aee0da156cfaaebe04d05f3ad851ce
diff --git a/inst/reveal.js-5.2.1/css/theme/source/ids.scss b/inst/reveal.js-5.2.1/css/theme/source/ids.scss
index e0a7634..e833b8f 100644
--- a/inst/reveal.js-5.2.1/css/theme/source/ids.scss
+++ b/inst/reveal.js-5.2.1/css/theme/source/ids.scss
@@ -202,11 +202,17 @@
 
 // Ordered lists: the CSS list-item counter honours the HTML start/value
 // attributes, so pandoc's `start`/`value` lists keep their numbers. Numbers
-// are right-aligned into the indent, ending just before the item text.
+// are anchored with their right edge $idsNumberGap away from the item text
+// (a shrink-to-fit box; anchoring from the right instead of a fixed-width,
+// right-aligned box, because over-wide numbers would otherwise overflow
+// INTO the text). Longer numbers like "10." grow to the left.
+$idsNumberGap: 0.3em;
+
 .reveal .slides section:not(.title-frame) ol > li::before {
   content: counter(list-item, decimal) "." !important;
-  width: calc(#{$idsListIndent} - 0.12em) !important;
-  text-align: right !important;
+  left: auto !important;
+  right: 100% !important;
+  margin-right: $idsNumberGap !important;
 }
 
 // Checkbox (pandoc task) lists keep their checkboxes, no markers on top.
@@ -1124,9 +1130,23 @@
 /*********************************************
  * OVERVIEW / TOC SLIDES
  *********************************************/
-/* Entries stay at the normal slide body size and keep the theme's link
-   colour, so they read as the navigation links they are. Only the denser
-   two-column variant with sub-headings is scaled down. */
+/* The auto-generated TOC lists the chapters with their numbers as
+   .ids-toc-number spans in the marker colour, prepended to the entry
+   titles: the enumeration therefore starts flush with the slide heading
+   (the drawn ol/ul markers are suppressed for the top-level entries;
+   sub-headings below their chapters keep their bullets). Entries stay at
+   the normal slide body size and keep the theme's link colour, so they
+   read as the navigation links they are. Only the denser two-column
+   variant with sub-headings is scaled down. */
+.reveal .slides section:not(.title-frame) .ids-overview-toc {
+  padding-left: 0 !important;
+}
+.reveal .slides section:not(.title-frame) .ids-overview-toc > li::before {
+  content: none !important;
+}
+.reveal .ids-overview-toc .ids-toc-number {
+  color: var(--r-list-bullet-color);
+}
 .reveal .ids-overview-toc {
   list-style: none;
   margin: 0.2em 0 0 0;
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 43fe710..dd54e86 100644
--- a/inst/reveal.js-5.2.1/dist/theme/ids.css
+++ b/inst/reveal.js-5.2.1/dist/theme/ids.css
@@ -415,8 +415,9 @@
 
 .reveal .slides section:not(.title-frame) ol > li::before {
   content: counter(list-item, decimal) "." !important;
-  width: calc(0.65em - 0.12em) !important;
-  text-align: right !important;
+  left: auto !important;
+  right: 100% !important;
+  margin-right: 0.3em !important;
 }
 
 .reveal .slides section:not(.title-frame) ul.task-list li::before {
@@ -1146,9 +1147,26 @@
 /*********************************************
  * OVERVIEW / TOC SLIDES
  *********************************************/
-/* Entries stay at the normal slide body size and keep the theme's link
-   colour, so they read as the navigation links they are. Only the denser
-   two-column variant with sub-headings is scaled down. */
+/* The auto-generated TOC lists the chapters with their numbers as
+   .ids-toc-number spans in the marker colour, prepended to the entry
+   titles: the enumeration therefore starts flush with the slide heading
+   (the drawn ol/ul markers are suppressed for the top-level entries;
+   sub-headings below their chapters keep their bullets). Entries stay at
+   the normal slide body size and keep the theme's link colour, so they
+   read as the navigation links they are. Only the denser two-column
+   variant with sub-headings is scaled down. */
+.reveal .slides section:not(.title-frame) .ids-overview-toc {
+  padding-left: 0 !important;
+}
+
+.reveal .slides section:not(.title-frame) .ids-overview-toc > li::before {
+  content: none !important;
+}
+
+.reveal .ids-overview-toc .ids-toc-number {
+  color: var(--r-list-bullet-color);
+}
+
 .reveal .ids-overview-toc {
   list-style: none;
   margin: 0.2em 0 0 0;
diff --git a/inst/rmarkdown/templates/revealjs_presentation/resources/default.html b/inst/rmarkdown/templates/revealjs_presentation/resources/default.html
index cf56f8e..45b55e4 100644
--- a/inst/rmarkdown/templates/revealjs_presentation/resources/default.html
+++ b/inst/rmarkdown/templates/revealjs_presentation/resources/default.html
@@ -1013,16 +1013,101 @@
 </script>
 $endif$
 <script>
+  // Chapter classification shared by the numbering and overview TOC scripts
+  // below (ported from the gdoc2reveal workflow): chapters are the level-1
+  // (pandoc "title-slide") sections of a deck. References, appendix and
+  // thank-you chapters are exempt from numbering and left out of the
+  // overview TOC.
+  window.idsChapters = (function () {
+    var EXCLUDED = ['references', 'literatur', 'literaturverzeichnis',
+      'referenzen', 'publikationen', 'herausgeberschaften', 'appendix',
+      'anhang'];
+    var THANK_YOU = /thank[\s\S]*you|vielen[\s\S]*dank/i;
+    // like the old pipeline: drop a leading "12.", "3)" or "-" plus
+    // whitespace, but leave headings like "3D printing" alone
+    var STRIP_NUMBER = /^\s*[-\d]+\s*(?:[.)]|\s)\s*/;
+
+    function isExcluded(title) {
+      var t = title.trim().toLowerCase();
+      return EXCLUDED.indexOf(t) !== -1 || THANK_YOU.test(t);
+    }
+
+    function isChapterSlide(slide) {
+      return !slide.classList.contains('title-frame') &&
+        slide.classList.contains('title-slide') &&
+        !!slide.querySelector('h1');
+    }
+
+    return {
+      isExcluded: isExcluded,
+      isChapterSlide: isChapterSlide,
+      stripNumber: function (title) { return title.replace(STRIP_NUMBER, ''); }
+    };
+  })();
+</script>
+<script>
+  // Automatic chapter numbering (ported from the gdoc2reveal workflow):
+  // chapter headings get a sequential "1. ", "2. " ... prefix, except
+  // references, appendix and thank-you chapters, which keep their bare
+  // titles and are not counted. Manually typed leading numbers are stripped
+  // first, so rendering twice or renumbering an already numbered deck stays
+  // stable. Runs before the overview TOC builder below: the menu plugin and
+  // the transcript view show the numbers, while the overview TOC strips
+  // them again and numbers its ordered list entries instead, like the old
+  // Google Docs workflow did.
+  (function () {
+    function firstTextNode(el) {
+      var n = el.firstChild;
+      while (n && n.nodeType !== Node.TEXT_NODE) n = n.nextSibling;
+      return n;
+    }
+
+    function numberChapters() {
+      if (typeof Reveal === 'undefined' || !Reveal.isReady()) return;
+      var n = 0;
+      Reveal.getSlides().forEach(function (slide) {
+        if (!window.idsChapters.isChapterSlide(slide)) return;
+        var h = slide.querySelector('h1');
+        if (window.idsChapters.isExcluded(h.textContent)) return;
+        n += 1;
+        slide.dataset.chapterNumber = n;
+        var t = firstTextNode(h);
+        if (!t) {
+          t = document.createTextNode('');
+          h.insertBefore(t, h.firstChild);
+        }
+        var label = window.idsChapters.stripNumber(t.nodeValue);
+        if (t === h.firstChild) {
+          t.nodeValue = n + '. ' + label;
+        } else {
+          // the heading starts with an element (e.g. <em>): put the number
+          // in front of it instead of into the first text node
+          t.nodeValue = label;
+          h.insertBefore(document.createTextNode(n + '. '), h.firstChild);
+        }
+      });
+    }
+
+    if (typeof Reveal !== 'undefined') {
+      if (Reveal.isReady()) numberChapters();
+      else Reveal.addEventListener('ready', numberChapters);
+    }
+  })();
+</script>
+<script>
   // Slides whose (only) heading is "Overview" or "Überblick" are
   // 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. References and appendix chapters
-  // (English and German) are left out. Styling in the ids theme.
+  // (English and German) are left out. The chapter numbers (set on the
+  // headings by the numbering script above) are prepended to the entry
+  // titles as .ids-toc-number spans, so the enumeration starts flush with
+  // the slide heading; the ids theme styles the spans in the marker colour
+  // and suppresses its own drawn list markers for the top-level entries.
   (function () {
     var TITLES = ['overview', 'überblick'];
-    var EXCLUDED = ['references', 'literatur', 'literaturverzeichnis', 'appendix', 'anhang'];
 
     function isOverviewSlide(slide) {
       var h = slide.querySelector('h1,h2');
@@ -1041,7 +1126,7 @@
         var includeSub = (heading.getAttribute('data-overview-subheadings') || '')
           .toLowerCase() !== 'false'
           && heading.hasAttribute('data-overview-subheadings');
-        var list = document.createElement('ul');
+        var list = document.createElement('ol');
         list.className = 'ids-overview-toc';
         if (includeSub) list.classList.add('with-subheadings');
         var subList = null;
@@ -1051,12 +1136,22 @@
           var h = slide.querySelector('h1,h2');
           if (!h) return;
           if (h.tagName === 'H1') {
-            excluded = EXCLUDED.indexOf(h.textContent.trim().toLowerCase()) !== -1;
+            excluded = window.idsChapters.isExcluded(h.textContent);
           }
           if (excluded || (h.tagName !== 'H1' && !includeSub)) return;
           var link = document.createElement('a');
           link.href = '#/' + slide.id;
-          link.textContent = h.textContent;
+          if (h.tagName === 'H1' && slide.dataset.chapterNumber) {
+            var number = document.createElement('span');
+            number.className = 'ids-toc-number';
+            number.textContent = slide.dataset.chapterNumber + '. ';
+            link.appendChild(number);
+            link.appendChild(document.createTextNode(
+              window.idsChapters.stripNumber(h.textContent)
+            ));
+          } else {
+            link.textContent = h.textContent;
+          }
           var item = document.createElement('li');
           item.appendChild(link);
           if (h.tagName === 'H1') {