@pretextbook/pretext-html 0.13.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,5 +3,5 @@
3
3
  "repository": "PreTeXtBook/pretext",
4
4
  "ref": "master"
5
5
  },
6
- "refreshed": "2026-08-20T14:22:45.334Z"
6
+ "refreshed": "2026-08-31T02:38:18.809Z"
7
7
  }
@@ -50,6 +50,14 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
50
50
  <!-- available for extraction. -->
51
51
  <xsl:variable name="exercise-style" select="'dynamic'"/>
52
52
 
53
+ <!-- A second Catch-22, of the same shape. A publisher may hold GeoGebra -->
54
+ <!-- applets behind a play button, but then this pass would photograph the -->
55
+ <!-- button rather than the applet, and the resulting image is what stands -->
56
+ <!-- in for the applet in static output. Worse, that image is exactly what -->
57
+ <!-- would be wanted to fill the empty preview a play button leaves for an -->
58
+ <!-- applet GeoGebra does not host. So the election is overridden here. -->
59
+ <xsl:variable name="b-geogebra-play-button" select="false()"/>
60
+
53
61
  <!-- @preview indicates custom image is present -->
54
62
  <!-- Stylesheet output is text, with "unique-id" -->
55
63
  <!-- of each interactive, one per line, to be -->
@@ -723,35 +723,6 @@
723
723
  <xsl:call-template name="begin-block">
724
724
  <xsl:with-param name="block-title">Header</xsl:with-param>
725
725
  </xsl:call-template>
726
- <xsl:if test="not($b-human-readable)">
727
- <!-- see select-latex-macros template -->
728
- <xsl:variable name="macros">
729
- <xsl:call-template name="select-latex-macros"/>
730
- </xsl:variable>
731
- <xsl:if test="$macros != ''">
732
- <xsl:variable name="wrapped-macros">
733
- <xsl:text>&lt;div style="display:none;">\(</xsl:text>
734
- <xsl:value-of select="$macros" />
735
- <xsl:text>\)&lt;/div></xsl:text>
736
- </xsl:variable>
737
- <xsl:variable name="delimiter">
738
- <xsl:call-template name="find-unused-character">
739
- <xsl:with-param name="string" select="$wrapped-macros"/>
740
- <!-- https://stackoverflow.com/questions/43617820/what-are-the-legal-delimiters-for-perl-5s-pick-your-own-quotes-operators -->
741
- <!-- NB: don't use (), <>, [], {}, because as perl delimiters, closer must be the right version; too complicated to check for -->
742
- <xsl:with-param name="charset" select="concat($apos,'|/?.,+-_~`!@$%^&amp;*')"/>
743
- </xsl:call-template>
744
- </xsl:variable>
745
- <xsl:text>TEXT(MODES(PTX=&gt;'',HTML=&gt;</xsl:text>
746
- <xsl:if test="$delimiter != $apos">
747
- <xsl:text>q</xsl:text>
748
- </xsl:if>
749
- <xsl:value-of select="$delimiter"/>
750
- <xsl:value-of select="$wrapped-macros"/>
751
- <xsl:value-of select="$delimiter"/>
752
- <xsl:text>));&#xa;</xsl:text>
753
- </xsl:if>
754
- </xsl:if>
755
726
  <xsl:text>&#xa;</xsl:text>
756
727
  </xsl:template>
757
728
 
@@ -1034,9 +1005,7 @@
1034
1005
  <xsl:value-of select="$standard-macros" />
1035
1006
  <xsl:value-of select="$implied-macros" />
1036
1007
  <xsl:value-of select="$user-macros" />
1037
- <xsl:if test=".//latex-image or ancestor::exercisegroup/introduction//latex-image">
1038
- <xsl:value-of select="$ptx-pg-macros" />
1039
- </xsl:if>
1008
+ <xsl:value-of select="$ptx-pg-macros" />
1040
1009
  <xsl:value-of select="$course-macros" />
1041
1010
  <xsl:text>);</xsl:text>
1042
1011
  <xsl:text>&#xa;</xsl:text>
@@ -1361,40 +1330,6 @@
1361
1330
  <xsl:text>&#xa;</xsl:text>
1362
1331
  </xsl:template>
1363
1332
 
1364
- <!-- Inside math we need to print definitions for PTX author-defined -->
1365
- <!-- LaTeX macros to support WeBWorK's images display mode. -->
1366
-
1367
- <!-- TODO: This named template examines the current context (see '.' in -->
1368
- <!-- contains() below), so should be a match template. But its recursive -->
1369
- <!-- implementation makes it a named template for now. -->
1370
- <xsl:template name="select-latex-macros">
1371
- <xsl:param name="macros" select="$latex-macros" />
1372
- <xsl:variable name="trimmed-start">
1373
- <xsl:if test="contains($macros, '\newcommand{')">
1374
- <xsl:value-of select="substring-after($macros, '\newcommand{')"/>
1375
- </xsl:if>
1376
- </xsl:variable>
1377
- <xsl:variable name="macro-name">
1378
- <xsl:if test="contains($trimmed-start, '}')">
1379
- <xsl:value-of select="substring-before($trimmed-start, '}')"/>
1380
- </xsl:if>
1381
- </xsl:variable>
1382
- <xsl:variable name="macro-command">
1383
- <xsl:value-of select="substring-before($macros, '&#xa;')"/>
1384
- </xsl:variable>
1385
- <xsl:variable name="next-lines">
1386
- <xsl:value-of select="substring-after($macros, '&#xa;')"/>
1387
- </xsl:variable>
1388
- <xsl:if test="contains(., $macro-name)">
1389
- <xsl:value-of select="normalize-space($macro-command)"/>
1390
- </xsl:if>
1391
- <xsl:if test="not($next-lines = '')">
1392
- <xsl:call-template name="select-latex-macros">
1393
- <xsl:with-param name="macros" select="$next-lines"/>
1394
- </xsl:call-template>
1395
- </xsl:if>
1396
- </xsl:template>
1397
-
1398
1333
  <!-- ##################################################################### -->
1399
1334
  <!-- ##################################################################### -->
1400
1335
  <!-- Above: templates for elements that only ever apply within a webwork -->
@@ -1490,9 +1425,6 @@
1490
1425
  <xsl:template match="m">
1491
1426
  <xsl:param name="b-human-readable" />
1492
1427
  <xsl:text>[`</xsl:text>
1493
- <xsl:if test="$b-human-readable">
1494
- <xsl:call-template name="select-latex-macros"/>
1495
- </xsl:if>
1496
1428
  <xsl:apply-templates select="text()|var" />
1497
1429
  <!-- look ahead to absorb immediate clause-ending punctuation -->
1498
1430
  <xsl:apply-templates select="." mode="get-clause-punctuation" />
@@ -1507,9 +1439,6 @@
1507
1439
  <xsl:call-template name="potential-list-indent" />
1508
1440
  </xsl:if>
1509
1441
  <xsl:text>[```</xsl:text>
1510
- <xsl:if test="$b-human-readable">
1511
- <xsl:call-template name="select-latex-macros"/>
1512
- </xsl:if>
1513
1442
  <!-- only a single mrow as a child (transition from old "me") -->
1514
1443
  <xsl:apply-templates select="mrow/text()|mrow/var" />
1515
1444
  <!-- look ahead to absorb immediate clause-ending punctuation -->
@@ -1591,9 +1520,6 @@
1591
1520
  <xsl:call-template name="potential-list-indent" />
1592
1521
  </xsl:if>
1593
1522
  <xsl:text>[```</xsl:text>
1594
- <xsl:if test="$b-human-readable">
1595
- <xsl:call-template name="select-latex-macros"/>
1596
- </xsl:if>
1597
1523
  <xsl:value-of select="$content" />
1598
1524
  <xsl:if test="ancestor::ul|ancestor::ol">
1599
1525
  <xsl:call-template name="potential-list-indent" />
@@ -482,13 +482,11 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
482
482
  <xsl:template match="node()|@*" mode="augment">
483
483
  <xsl:param name="parent-struct" select="''"/>
484
484
  <xsl:param name="level" select="0"/>
485
- <xsl:param name="ordered-list-level" select="0"/>
486
485
 
487
486
  <xsl:copy>
488
487
  <xsl:apply-templates select="node()|@*" mode="augment">
489
488
  <xsl:with-param name="parent-struct" select="$parent-struct"/>
490
489
  <xsl:with-param name="level" select="$level"/>
491
- <xsl:with-param name="ordered-list-level" select="$ordered-list-level"/>
492
490
  </xsl:apply-templates>
493
491
  </xsl:copy>
494
492
  </xsl:template>
@@ -3613,6 +3611,25 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
3613
3611
  </xsl:if>
3614
3612
  </xsl:template>
3615
3613
 
3614
+ <!-- A list ("ol", "ul", "dl") may stand bare at the top level -->
3615
+ <!-- of a "slide" or "subslide", an authoring convenience -->
3616
+ <!-- particular to the slideshow genre. Internally we keep the -->
3617
+ <!-- main grammar's single shape - a list lives within a -->
3618
+ <!-- paragraph - so every later consumer of the tree meets only -->
3619
+ <!-- one form. The manufactured paragraph does not survive to -->
3620
+ <!-- output as an HTML "p": the Reveal.js conversion explodes -->
3621
+ <!-- any paragraph containing displays, and for a paragraph that -->
3622
+ <!-- is exactly one list the explosion is precisely the bare -->
3623
+ <!-- list again. A @pause stays on the list itself, where the -->
3624
+ <!-- list item templates consult it. -->
3625
+ <xsl:template match="slide/ol|slide/ul|slide/dl|subslide/ol|subslide/ul|subslide/dl" mode="repair">
3626
+ <p>
3627
+ <xsl:copy>
3628
+ <xsl:apply-templates select="node()|@*" mode="repair"/>
3629
+ </xsl:copy>
3630
+ </p>
3631
+ </xsl:template>
3632
+
3616
3633
 
3617
3634
  <!-- ############################## -->
3618
3635
  <!-- Killed, in Chronological Order -->
@@ -3660,6 +3677,28 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
3660
3677
  <!-- whole, and the deprecation warning says so plainly -->
3661
3678
  <xsl:template match="references/conclusion" mode="repair"/>
3662
3679
 
3680
+ <!-- 2026-08-20 "ol" structuring an "exercise" is deprecated -->
3681
+
3682
+ <!-- An "ol" as a child of an "exercise", or of its "statement", -->
3683
+ <!-- once lettered its items like exercise parts. The list is -->
3684
+ <!-- wrapped in a "p" here, like any other list in running prose. -->
3685
+ <!-- Within the divisions that historically lettered these lists, -->
3686
+ <!-- a stamp preserves the extra list level for existing sources; -->
3687
+ <!-- new sources letter a list with marker="(a)", or structure -->
3688
+ <!-- real parts with "task". The deprecation warning says so. -->
3689
+ <xsl:template match="exercise/ol|exercise/statement/ol" mode="repair">
3690
+ <p>
3691
+ <xsl:copy>
3692
+ <xsl:if test="ancestor::exercises or ancestor::reading-questions or ancestor::worksheet or ancestor::handout">
3693
+ <xsl:attribute name="pi:legacy-parts">
3694
+ <xsl:text>yes</xsl:text>
3695
+ </xsl:attribute>
3696
+ </xsl:if>
3697
+ <xsl:apply-templates select="node()|@*" mode="repair"/>
3698
+ </xsl:copy>
3699
+ </p>
3700
+ </xsl:template>
3701
+
3663
3702
  <!-- ########## -->
3664
3703
  <!-- Enrichment -->
3665
3704
  <!-- ########## -->
@@ -4034,7 +4073,6 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
4034
4073
  <xsl:template match="part|chapter|appendix|section|subsection|subsubsection|exercises|solutions|reading-questions|references|glossary|worksheet|handout" mode="augment">
4035
4074
  <xsl:param name="parent-struct"/>
4036
4075
  <xsl:param name="level"/>
4037
- <xsl:param name="ordered-list-level" />
4038
4076
 
4039
4077
  <xsl:variable name="the-serial">
4040
4078
  <xsl:apply-templates select="." mode="division-serial-number"/>
@@ -4084,16 +4122,6 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
4084
4122
  </xsl:choose>
4085
4123
  </xsl:variable>
4086
4124
  <xsl:variable name="next-level" select="$level + 1"/>
4087
- <xsl:variable name="next-ordered-list-level">
4088
- <xsl:choose>
4089
- <xsl:when test="self::exercises or self::worksheet or self::handout or self::reading-questions or self::references">
4090
- <xsl:number value="1" />
4091
- </xsl:when>
4092
- <xsl:otherwise>
4093
- <xsl:number value="0" />
4094
- </xsl:otherwise>
4095
- </xsl:choose>
4096
- </xsl:variable>
4097
4125
  <xsl:copy>
4098
4126
  <xsl:attribute name="pi:struct">
4099
4127
  <xsl:value-of select="$parent-struct"/>
@@ -4114,7 +4142,6 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
4114
4142
  <xsl:apply-templates select="node()|@*" mode="augment">
4115
4143
  <xsl:with-param name="parent-struct" select="$new-struct"/>
4116
4144
  <xsl:with-param name="level" select="$next-level"/>
4117
- <xsl:with-param name="ordered-list-level" select="$next-ordered-list-level"/>
4118
4145
  </xsl:apply-templates>
4119
4146
  </xsl:copy>
4120
4147
  </xsl:template>
@@ -4130,7 +4157,6 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
4130
4157
  <xsl:apply-templates select="node()|@*" mode="augment">
4131
4158
  <xsl:with-param name="parent-struct" select="''"/>
4132
4159
  <xsl:with-param name="level" select="0"/>
4133
- <xsl:with-param name="ordered-list-level" select="0"/>
4134
4160
  </xsl:apply-templates>
4135
4161
  </xsl:copy>
4136
4162
  </xsl:template>
@@ -4201,7 +4227,7 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
4201
4227
  <xsl:when test="$level='2'">i</xsl:when>
4202
4228
  <xsl:when test="$level='3'">A</xsl:when>
4203
4229
  <xsl:otherwise>
4204
- <xsl:message>PTX:ERROR: ordered list is more than 4 levels deep (at level <xsl:value-of select="$level" />) or is inside an "exercise" and is more than 3 levels deep (at level <xsl:value-of select="$level - 1" />)</xsl:message>
4230
+ <xsl:message>PTX:ERROR: ordered list is more than 4 levels deep (at level <xsl:value-of select="$level" />; a legacy exercise-parts list counts one level extra)</xsl:message>
4205
4231
  </xsl:otherwise>
4206
4232
  </xsl:choose>
4207
4233
  </xsl:otherwise>
@@ -4209,8 +4235,11 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
4209
4235
  </xsl:template>
4210
4236
 
4211
4237
  <xsl:template match="ol" mode="augment">
4212
- <xsl:param name="ordered-list-level"/>
4213
- <xsl:variable name="next-level" select="$ordered-list-level + 1" />
4238
+ <!-- The list level is intrinsic: a count of enclosing ordered -->
4239
+ <!-- lists, raised by one within a list the repair phase stamped -->
4240
+ <!-- as a legacy exercise-parts list (the stamp lifts the whole -->
4241
+ <!-- nest, so sublists letter as they always have). -->
4242
+ <xsl:variable name="ordered-list-level" select="count(ancestor::ol) + number(boolean(ancestor-or-self::ol[@pi:legacy-parts]))"/>
4214
4243
  <xsl:variable name="format-code">
4215
4244
  <xsl:apply-templates select="." mode="format-code">
4216
4245
  <xsl:with-param name="level" select="$ordered-list-level"/>
@@ -4256,9 +4285,62 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
4256
4285
  <xsl:attribute name="pi:marker-suffix">
4257
4286
  <xsl:value-of select="$marker-suffix"/>
4258
4287
  </xsl:attribute>
4259
- <xsl:apply-templates select="node()|@*" mode="augment">
4260
- <xsl:with-param name="ordered-list-level" select="$next-level"/>
4288
+ <xsl:apply-templates select="node()|@*" mode="augment"/>
4289
+ </xsl:copy>
4290
+ </xsl:template>
4291
+
4292
+ <!-- The stamp has served its purpose once the level is recorded, -->
4293
+ <!-- so it does not survive into the assembled tree. -->
4294
+ <xsl:template match="@pi:legacy-parts" mode="augment"/>
4295
+
4296
+ <!-- List levels and format codes are ancestor-axis computations -->
4297
+ <!-- that *could* be made at conversion time in -common; we simply -->
4298
+ <!-- choose to make them once, here in assembly, and stamp the -->
4299
+ <!-- results. Unordered lists follow the pattern of ordered lists -->
4300
+ <!-- above: an authored @marker wins, else defaults cycle by depth. -->
4301
+
4302
+ <!-- Labels of unordered lists have format codes: -->
4303
+ <!-- disc, circle, square, or none. Default order: -->
4304
+ <!-- disc, circle, square, disc. -->
4305
+ <xsl:template match="ul" mode="format-code">
4306
+ <xsl:param name="level"/>
4307
+ <xsl:choose>
4308
+ <xsl:when test="@marker">
4309
+ <xsl:choose>
4310
+ <xsl:when test="@marker='disc'">disc</xsl:when>
4311
+ <xsl:when test="@marker='circle'">circle</xsl:when>
4312
+ <xsl:when test="@marker='square'">square</xsl:when>
4313
+ <xsl:when test="@marker=''">none</xsl:when>
4314
+ <xsl:otherwise>
4315
+ <xsl:message>PTX:ERROR: unordered list label (<xsl:value-of select="@marker" />) not recognized</xsl:message>
4316
+ </xsl:otherwise>
4317
+ </xsl:choose>
4318
+ </xsl:when>
4319
+ <xsl:otherwise>
4320
+ <xsl:choose>
4321
+ <xsl:when test="$level='0'">disc</xsl:when>
4322
+ <xsl:when test="$level='1'">circle</xsl:when>
4323
+ <xsl:when test="$level='2'">square</xsl:when>
4324
+ <xsl:when test="$level='3'">disc</xsl:when>
4325
+ <xsl:otherwise>
4326
+ <xsl:message>PTX:ERROR: unordered list is more than 4 levels deep (at level <xsl:value-of select="$level" />)</xsl:message>
4327
+ </xsl:otherwise>
4328
+ </xsl:choose>
4329
+ </xsl:otherwise>
4330
+ </xsl:choose>
4331
+ </xsl:template>
4332
+
4333
+ <xsl:template match="ul" mode="augment">
4334
+ <xsl:variable name="format-code">
4335
+ <xsl:apply-templates select="." mode="format-code">
4336
+ <xsl:with-param name="level" select="count(ancestor::ul)"/>
4261
4337
  </xsl:apply-templates>
4338
+ </xsl:variable>
4339
+ <xsl:copy>
4340
+ <xsl:attribute name="pi:format-code">
4341
+ <xsl:value-of select="$format-code"/>
4342
+ </xsl:attribute>
4343
+ <xsl:apply-templates select="node()|@*" mode="augment"/>
4262
4344
  </xsl:copy>
4263
4345
  </xsl:template>
4264
4346
 
@@ -4852,13 +4934,21 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
4852
4934
  <!-- we want the stylesheet to be independent, and the template is -->
4853
4935
  <!-- also applied here. -->
4854
4936
 
4855
- <!-- Every element carrying an "@pi:assembly-id" stamp reports it -->
4856
- <!-- through this single template. The match list is deliberately -->
4857
- <!-- explicit — media and images, data files, STACK problems, and -->
4858
- <!-- the fill-in dynamic-substitution owners (the exercise-like -->
4859
- <!-- elements, or a "task" they contain, whichever holds "setup") -->
4860
- <!-- — so the BUG fallback below can catch an unexpected -->
4861
- <!-- application. -->
4937
+ <!-- Every element carrying an "@pi:assembly-id" stamp reports it -->
4938
+ <!-- through this single template. The match list is deliberately -->
4939
+ <!-- explicit — media and images, data files, STACK problems, and -->
4940
+ <!-- the fill-in dynamic-substitution owners (the exercise-like -->
4941
+ <!-- elements, or a "task" they contain, whichever holds "setup") -->
4942
+ <!-- — so the BUG fallback below can catch an unexpected -->
4943
+ <!-- application. -->
4944
+ <!-- -->
4945
+ <!-- The stamp belongs to the assembly, laid down partway through -->
4946
+ <!-- the phases so that the later ones can coordinate on it. It is -->
4947
+ <!-- read here, and in the extraction stylesheets that feed those -->
4948
+ <!-- phases, and nowhere else. A conversion wants the "unique-id" -->
4949
+ <!-- template instead: the two agree for most elements but not for -->
4950
+ <!-- all, so a conversion reading this value can form a name that -->
4951
+ <!-- nothing else in the build uses. -->
4862
4952
  <xsl:template match="audio|video|interactive|image
4863
4953
  | datafile
4864
4954
  | exercise/stack
@@ -1209,9 +1209,7 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
1209
1209
  </xsl:template>
1210
1210
 
1211
1211
  <xsl:template match="ul/li" mode="unicode-list-marker">
1212
- <xsl:variable name="format-code">
1213
- <xsl:apply-templates select="parent::ul" mode="format-code"/>
1214
- </xsl:variable>
1212
+ <xsl:variable name="format-code" select="parent::ul/@pi:format-code"/>
1215
1213
  <!-- The list label. The file en-ueb-chardefs.uti -->
1216
1214
  <!-- associates these Unicode values with the indicated -->
1217
1215
  <!-- dot patterns. This jibes with [BANA-2016, 8.6.2], -->
@@ -5122,61 +5122,6 @@ Book (with parts), "section" at level 3
5122
5122
  <!-- Utility templates to determine the depth -->
5123
5123
  <!-- of a list, relative to nesting in other lists -->
5124
5124
 
5125
- <!-- We determine the depth of an unordered -->
5126
- <!-- list, relative only to other unordered -->
5127
- <!-- lists in a nesting, so as to determine -->
5128
- <!-- the right label to apply, esp. as defaults -->
5129
- <!-- The recursive template should be called -->
5130
- <!-- without a level, since it defaults to zero -->
5131
- <xsl:template match="ul" mode="unordered-list-level">
5132
- <!-- Start with level zero, and increment on successive calls -->
5133
- <xsl:param name="level" select="0"/>
5134
- <xsl:choose>
5135
- <!-- Another unordered list above, add one and recurse -->
5136
- <xsl:when test="ancestor::ul">
5137
- <xsl:apply-templates select="ancestor::ul[1]" mode="unordered-list-level">
5138
- <xsl:with-param name="level" select="$level + 1" />
5139
- </xsl:apply-templates>
5140
- </xsl:when>
5141
- <!-- No unordered list above, done, so return level -->
5142
- <xsl:otherwise>
5143
- <xsl:value-of select="$level" />
5144
- </xsl:otherwise>
5145
- </xsl:choose>
5146
- </xsl:template>
5147
-
5148
- <!-- Ordered lists follow the same strategy, -->
5149
- <!-- except we implement exercises and references -->
5150
- <!-- elements as ordered lists, so we need to absorb -->
5151
- <!-- them into the general treatment of nested lists -->
5152
- <!-- They do only occur as top-level elements, so that -->
5153
- <!-- assumption allows for some economy -->
5154
- <xsl:template match="ol" mode="ordered-list-level">
5155
- <xsl:param name="level" select="0"/>
5156
- <xsl:choose>
5157
- <!-- Since exercises divisions and references are top-level -->
5158
- <!-- ordered lists, when these are the only interesting -->
5159
- <!-- ancestor, we add one to the level and return -->
5160
- <xsl:when test="(ancestor::exercises or ancestor::worksheet or ancestor::handout or ancestor::reading-questions or ancestor::references) and not(ancestor::ol)">
5161
- <xsl:value-of select="$level + 1" />
5162
- </xsl:when>
5163
- <xsl:when test="ancestor::ol">
5164
- <xsl:apply-templates select="ancestor::ol[1]" mode="ordered-list-level">
5165
- <xsl:with-param name="level" select="$level + 1" />
5166
- </xsl:apply-templates>
5167
- </xsl:when>
5168
- <xsl:otherwise>
5169
- <xsl:value-of select="$level" />
5170
- </xsl:otherwise>
5171
- </xsl:choose>
5172
- </xsl:template>
5173
-
5174
- <!-- Exercises and References are -->
5175
- <!-- specialized top-level ordered lists -->
5176
- <xsl:template match="exercises|worksheet|handout|reading-questions|references" mode="ordered-list-level">
5177
- <xsl:value-of select="0" />
5178
- </xsl:template>
5179
-
5180
5125
  <!-- To indent properly in markdown, we -->
5181
5126
  <!-- need to count every type of list -->
5182
5127
  <xsl:template match="*" mode="list-level">
@@ -5205,41 +5150,6 @@ Book (with parts), "section" at level 3
5205
5150
  </xsl:choose>
5206
5151
  </xsl:template>
5207
5152
 
5208
- <!-- Labels of unordered list have formatting codes, which -->
5209
- <!-- we detect here and pass on to other more specialized -->
5210
- <!-- templates for implementation specifics -->
5211
- <!-- disc, circle, square or blank are the options -->
5212
- <!-- Default order: disc, circle, square, disc -->
5213
- <xsl:template match="ul" mode="format-code">
5214
- <xsl:choose>
5215
- <xsl:when test="@marker">
5216
- <xsl:choose>
5217
- <xsl:when test="@marker='disc'">disc</xsl:when>
5218
- <xsl:when test="@marker='circle'">circle</xsl:when>
5219
- <xsl:when test="@marker='square'">square</xsl:when>
5220
- <xsl:when test="@marker=''">none</xsl:when>
5221
- <xsl:otherwise>
5222
- <xsl:message>PTX:ERROR: unordered list label (<xsl:value-of select="@marker" />) not recognized</xsl:message>
5223
- </xsl:otherwise>
5224
- </xsl:choose>
5225
- </xsl:when>
5226
- <xsl:otherwise>
5227
- <xsl:variable name="level">
5228
- <xsl:apply-templates select="." mode="unordered-list-level" />
5229
- </xsl:variable>
5230
- <xsl:choose>
5231
- <xsl:when test="$level='0'">disc</xsl:when>
5232
- <xsl:when test="$level='1'">circle</xsl:when>
5233
- <xsl:when test="$level='2'">square</xsl:when>
5234
- <xsl:when test="$level='3'">disc</xsl:when>
5235
- <xsl:otherwise>
5236
- <xsl:message>PTX:ERROR: unordered list is more than 4 levels deep (at level <xsl:value-of select="$level" />)</xsl:message>
5237
- </xsl:otherwise>
5238
- </xsl:choose>
5239
- </xsl:otherwise>
5240
- </xsl:choose>
5241
- </xsl:template>
5242
-
5243
5153
  <!-- CSS class for multi-column lists. Context is an element with -->
5244
5154
  <!-- a potential "cols" attribute. Return value is "colsN" with -->
5245
5155
  <!-- 2 <= N <= 6, or empty when @cols is absent. -->
@@ -12030,6 +11940,41 @@ http://andrewmccarthy.ie/2014/11/06/swung-dash-in-latex/
12030
11940
  <xsl:with-param name="message" select="'a &quot;references&quot; division no longer has a &quot;conclusion&quot;. The element is discarded entirely: its content will not appear in any output. Please relocate the content, perhaps following the &quot;references&quot;'"/>
12031
11941
  </xsl:call-template>
12032
11942
  <!-- -->
11943
+ <!-- 2026-08-20 "ol" as a child of "exercise" is obsolete -->
11944
+ <xsl:call-template name="deprecation-message">
11945
+ <xsl:with-param name="occurrences" select="&quot;$document-root//exercise/ol&quot;" />
11946
+ <xsl:with-param name="date-string" select="'2026-08-20'" />
11947
+ <xsl:with-param name="message" select="'an &quot;ol&quot; is no longer a child of an &quot;exercise&quot;; a list belongs inside a &quot;p&quot;. We will wrap it for you, and lettered exercise parts will letter as before. Please convert: structure true parts with &quot;task&quot;, letter a plain list with marker=&quot;(a)&quot;, or consider an &quot;exercisegroup&quot; and its cols attribute for a compact multi-column layout'"/>
11948
+ </xsl:call-template>
11949
+ <!-- -->
11950
+ <!-- 2026-08-20 "ol" as a child of an exercise "statement" is obsolete -->
11951
+ <xsl:call-template name="deprecation-message">
11952
+ <xsl:with-param name="occurrences" select="&quot;$document-root//exercise/statement/ol&quot;" />
11953
+ <xsl:with-param name="date-string" select="'2026-08-20'" />
11954
+ <xsl:with-param name="message" select="'an &quot;ol&quot; is no longer a child of an &quot;exercise&quot; &quot;statement&quot;; a list belongs inside a &quot;p&quot;. We will wrap it for you, and lettered exercise parts will letter as before. Please convert: structure true parts with &quot;task&quot;, letter a plain list with marker=&quot;(a)&quot;, or consider an &quot;exercisegroup&quot; and its cols attribute for a compact multi-column layout'"/>
11955
+ </xsl:call-template>
11956
+ <!-- -->
11957
+ <!-- 2026-08-23 a "sidebyside" requires at least two panels -->
11958
+ <xsl:call-template name="deprecation-message">
11959
+ <xsl:with-param name="occurrences" select="&quot;$document-root//sidebyside[count(*) = 1][not(ancestor::interactive)]&quot;" />
11960
+ <xsl:with-param name="date-string" select="'2026-08-23'" />
11961
+ <xsl:with-param name="message" select="'a &quot;sidebyside&quot; now requires at least two panels. Conversions will continue to honor a single panel until at least 2028-08-23. Please convert: to size or center a lone &quot;image&quot;, use its &quot;width&quot; attribute; otherwise place the lone item outside any &quot;sidebyside&quot;'"/>
11962
+ </xsl:call-template>
11963
+ <!-- -->
11964
+ <!-- 2026-08-23 one "sidebyside" may not nest within another -->
11965
+ <xsl:call-template name="deprecation-message">
11966
+ <xsl:with-param name="occurrences" select="&quot;$document-root//sidebyside//sidebyside&quot;" />
11967
+ <xsl:with-param name="date-string" select="'2026-08-23'" />
11968
+ <xsl:with-param name="message" select="'one &quot;sidebyside&quot; may not appear within another, no matter how much structure intervenes. A nested &quot;sidebyside&quot; may behave unpredictably. Please relocate the inner &quot;sidebyside&quot; outside the outer one'"/>
11969
+ </xsl:call-template>
11970
+ <!-- -->
11971
+ <!-- 2026-08-23 an "sbsgroup" requires at least two "sidebyside" -->
11972
+ <xsl:call-template name="deprecation-message">
11973
+ <xsl:with-param name="occurrences" select="&quot;$document-root//sbsgroup[count(sidebyside) = 1]&quot;" />
11974
+ <xsl:with-param name="date-string" select="'2026-08-23'" />
11975
+ <xsl:with-param name="message" select="'an &quot;sbsgroup&quot; now requires at least two &quot;sidebyside&quot;. A group of one behaves exactly like the &quot;sidebyside&quot; alone, so use the &quot;sidebyside&quot; by itself, with the layout attributes moved onto it'"/>
11976
+ </xsl:call-template>
11977
+ <!-- -->
12033
11978
  </xsl:template>
12034
11979
 
12035
11980
  <!-- Miscellaneous -->
@@ -819,6 +819,14 @@
819
819
  <link href="../{$css-dir}/epub.css" rel="stylesheet" type="text/css"/>
820
820
  </xsl:otherwise>
821
821
  </xsl:choose>
822
+ <!-- Custom list markers are "@counter-style" rules derived from -->
823
+ <!-- the source, so they ride along in every content file's head -->
824
+ <xsl:if test="$b-needs-custom-marker-css">
825
+ <style>
826
+ <xsl:text>&#xa;</xsl:text>
827
+ <xsl:apply-templates select="exsl:node-set($ol-markers)//ol-marker" mode="ol-marker-style"/>
828
+ </style>
829
+ </xsl:if>
822
830
  </xsl:template>
823
831
 
824
832
  <!-- ############# -->
@@ -585,6 +585,16 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
585
585
  role="H1">
586
586
  <xsl:apply-templates select="." mode="link-id-attribute"/>
587
587
  <xsl:apply-templates select="." mode="title-full"/>
588
+ <!-- A subtitle belongs to the heading, as it does in HTML, so it -->
589
+ <!-- sits inside this block rather than following it. Lighter and -->
590
+ <!-- smaller than the title, and on a line of its own. -->
591
+ <xsl:if test="subtitle">
592
+ <fo:block font-size="60%"
593
+ font-weight="normal"
594
+ space-before="0.5em">
595
+ <xsl:apply-templates select="." mode="subtitle"/>
596
+ </fo:block>
597
+ </xsl:if>
588
598
  </fo:block>
589
599
  </xsl:template>
590
600
 
@@ -2046,13 +2056,11 @@ along with PreTeXt. If not, see <http://www.gnu.org/licenses/>.
2046
2056
  <xsl:value-of select="parent::ol/@pi:marker-suffix"/>
2047
2057
  </xsl:template>
2048
2058
 
2049
- <!-- Bullets via the "format-code" machinery of pretext-common.xsl, -->
2050
- <!-- which cycles disc, circle, square by level, and honors an -->
2051
- <!-- authored @marker (the empty @marker giving no bullet at all). -->
2059
+ <!-- Bullets via the format code stamped during assembly, which -->
2060
+ <!-- cycles disc, circle, square by level, and honors an authored -->
2061
+ <!-- @marker (the empty @marker giving no bullet at all). -->
2052
2062
  <xsl:template match="ul/li" mode="list-label">
2053
- <xsl:variable name="format-code">
2054
- <xsl:apply-templates select="parent::ul" mode="format-code"/>
2055
- </xsl:variable>
2063
+ <xsl:variable name="format-code" select="parent::ul/@pi:format-code"/>
2056
2064
  <xsl:choose>
2057
2065
  <xsl:when test="$format-code = 'disc'">
2058
2066
  <xsl:text>&#x2022;</xsl:text>