@abreen/tada 1.17.0 → 1.18.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.
Files changed (107) hide show
  1. package/README.md +41 -7
  2. package/bin/tada.ts +23 -9
  3. package/bin/validators.ts +34 -1
  4. package/build/branding.ts +41 -0
  5. package/build/build-manifest.ts +10 -5
  6. package/build/{watch/compiler-types.d.ts → build-types.d.ts} +8 -5
  7. package/build/build-validation.ts +176 -0
  8. package/build/bundle.ts +75 -65
  9. package/build/columns-plugin.ts +27 -1
  10. package/build/config-loader.ts +3 -3
  11. package/build/custom-fonts.ts +8 -5
  12. package/build/deflist-id-plugin.ts +2 -2
  13. package/build/generate-favicon.ts +7 -6
  14. package/build/generate-fonts.ts +8 -15
  15. package/build/generate-katex-assets.ts +19 -17
  16. package/build/generate-web-app-manifest.ts +3 -11
  17. package/build/lodash-template.ts +14 -0
  18. package/build/output-publication.ts +238 -0
  19. package/build/pagefind.ts +49 -21
  20. package/build/pdf-text.ts +41 -1
  21. package/build/pipeline.ts +95 -101
  22. package/build/site-assets.ts +46 -0
  23. package/build/site-build.ts +267 -0
  24. package/build/source-model.ts +223 -229
  25. package/build/source-records.ts +69 -81
  26. package/build/template-globals.ts +3 -0
  27. package/build/templates.ts +12 -8
  28. package/build/toc-plugin.ts +21 -21
  29. package/build/types.d.ts +38 -38
  30. package/build/util.ts +0 -4
  31. package/build/utils/code.ts +51 -117
  32. package/build/utils/final-html.ts +171 -47
  33. package/build/utils/jdi-runner/LiterateRunner.java +9 -1
  34. package/build/utils/literate-java.ts +11 -7
  35. package/build/utils/markdown-partials.ts +4 -3
  36. package/build/utils/markdown.ts +6 -17
  37. package/build/utils/paths.ts +3 -0
  38. package/build/utils/plain-text.ts +18 -0
  39. package/build/utils/render.ts +197 -154
  40. package/build/utils/shiki-highlighter.ts +11 -0
  41. package/build/utils/source-template.ts +2 -2
  42. package/build/utils/trace-core.ts +8 -83
  43. package/build/utils/trace.ts +38 -95
  44. package/build/validate-config-links.ts +39 -6
  45. package/build/watch/build-incremental.ts +46 -103
  46. package/build/watch/compiler.ts +158 -37
  47. package/build/watch/engine.ts +207 -162
  48. package/build/watch/index.ts +9 -2
  49. package/build/watch/planner.ts +88 -144
  50. package/build/watch/runtime.ts +21 -34
  51. package/build/watch/types.d.ts +32 -64
  52. package/package.json +4 -5
  53. package/schema/site.schema.json +18 -2
  54. package/src/_content.scss +447 -0
  55. package/src/_fonts.scss +90 -0
  56. package/src/_layout.scss +50 -35
  57. package/src/appearance-picker/{style.scss → _index.scss} +1 -1
  58. package/src/code/{style.scss → _index.scss} +61 -0
  59. package/src/header/{_base.scss → _index.scss} +98 -6
  60. package/src/index.ts +1 -17
  61. package/src/navigate/{style.scss → _index.scss} +20 -0
  62. package/src/navigate/eligible.ts +0 -40
  63. package/src/navigate/index.ts +50 -5
  64. package/src/navigate/lifecycle.ts +19 -2
  65. package/src/navigate/runtime.ts +20 -4
  66. package/src/page-update/index.ts +33 -20
  67. package/src/print/{style.scss → _index.scss} +3 -1
  68. package/src/search/index.ts +30 -2
  69. package/src/slides/{style.scss → _index.scss} +5 -0
  70. package/src/style.scss +24 -536
  71. package/src/timezone/index.ts +29 -3
  72. package/src/timezone/time-format.ts +4 -0
  73. package/src/toc/index.ts +6 -5
  74. package/src/trace/index.ts +11 -1
  75. package/templates/_author.html +2 -2
  76. package/templates/_download.html +1 -1
  77. package/templates/_footer.html +1 -1
  78. package/templates/_heading.html +3 -3
  79. package/templates/_nav.html +2 -2
  80. package/templates/_page-bottom.html +6 -0
  81. package/templates/_top.html +18 -14
  82. package/templates/code.html +22 -21
  83. package/templates/default.html +12 -11
  84. package/templates/literate.html +6 -5
  85. package/build/copy.ts +0 -82
  86. package/build/generate-content-assets.ts +0 -121
  87. package/build/utils/content-files.ts +0 -170
  88. package/build/watch/assets.ts +0 -103
  89. package/build/watch/build-full.ts +0 -97
  90. package/build/watch/build-helpers.ts +0 -66
  91. package/build/watch/build-result.ts +0 -19
  92. package/build/watch/fs-commit.ts +0 -153
  93. package/build/watch/mutations.ts +0 -73
  94. package/build/watch/snapshot.ts +0 -201
  95. package/build/watch/validation.ts +0 -50
  96. package/src/code.scss +0 -60
  97. package/src/critical.scss +0 -5
  98. package/src/header/style.scss +0 -91
  99. /package/src/{literate.scss → _literate.scss} +0 -0
  100. /package/src/{material-symbols.scss → _material-symbols.scss} +0 -0
  101. /package/src/anchor/{style.scss → _index.scss} +0 -0
  102. /package/src/page-update/{style.scss → _index.scss} +0 -0
  103. /package/src/question/{style.scss → _index.scss} +0 -0
  104. /package/src/search/{style.scss → _index.scss} +0 -0
  105. /package/src/timezone/{style.scss → _index.scss} +0 -0
  106. /package/src/toc/{style.scss → _index.scss} +0 -0
  107. /package/src/trace/{style.scss → _index.scss} +0 -0
@@ -93,6 +93,7 @@ body.is-presenting [data-slides-annotation-layer] {
93
93
  body.is-presenting header,
94
94
  body.is-presenting .title-and-info,
95
95
  body.is-presenting nav.toc,
96
+ body.is-presenting .page-bottom,
96
97
  body.is-presenting footer,
97
98
  body.is-presenting .appearance-pickers,
98
99
  body.is-presenting .slides-header,
@@ -177,3 +178,7 @@ body.is-presenting .slide:has(> .trace-widget) > .trace-widget > .trace-body {
177
178
  height: 100%;
178
179
  min-height: 0;
179
180
  }
181
+
182
+ body.is-presenting > .container {
183
+ min-height: 0;
184
+ }
package/src/style.scss CHANGED
@@ -1,540 +1,28 @@
1
- @use 'mixins' as *;
1
+ // The only Sass entry point, imported by src/index.ts and compiled once per
2
+ // build into index.bundle.*.css. Every other Sass file is a partial loaded
3
+ // here: shared partials live in src/, component styles in
4
+ // src/<component>/_index.scss. Modules are emitted in this order, so later
5
+ // rules win specificity ties.
6
+
7
+ // Theme custom properties, generated from the site config at build time
2
8
  @use 'config/theme';
3
9
  @use 'base';
4
10
  @use 'layout';
5
11
  @use 'alerts';
6
- @include theme.custom-font-faces;
7
- @include theme.custom-font-tuning;
8
-
9
- @font-face {
10
- font-family: Inter;
11
- font-style: normal;
12
- font-weight: 100 900;
13
- src: url('inter/InterVariable.woff2') format('woff2');
14
- font-display: swap;
15
- }
16
-
17
- @font-face {
18
- font-family: Inter;
19
- font-style: italic;
20
- font-weight: 100 900;
21
- src: url('inter/InterVariable-Italic.woff2') format('woff2');
22
- font-display: swap;
23
- }
24
-
25
- @font-face {
26
- font-family: 'Google Sans Code';
27
- font-style: normal;
28
- font-weight: 300 800;
29
- src: url('google-sans-code/GoogleSansCodeVariable.woff2') format('woff2');
30
- font-display: swap;
31
- }
32
-
33
- @font-face {
34
- font-family: 'Google Sans Code';
35
- font-style: italic;
36
- font-weight: 300 800;
37
- src: url('google-sans-code/GoogleSansCodeVariable-Italic.woff2')
38
- format('woff2');
39
- font-display: swap;
40
- }
41
-
42
- @font-face {
43
- font-family: 'Source Serif 4';
44
- font-style: normal;
45
- font-weight: 200 900;
46
- src: url('source-serif-4/SourceSerif4-VariableFont_opsz,wght.woff2')
47
- format('woff2');
48
- font-display: swap;
49
- }
50
-
51
- @font-face {
52
- font-family: 'Source Serif 4';
53
- font-style: italic;
54
- font-weight: 200 900;
55
- src: url('source-serif-4/SourceSerif4-Italic-VariableFont_opsz,wght.woff2')
56
- format('woff2');
57
- font-display: swap;
58
- }
59
-
60
- @font-face {
61
- font-family: 'Courier Prime';
62
- font-style: normal;
63
- font-weight: 400;
64
- src: url('courier-prime/CourierPrime-Regular.woff2') format('woff2');
65
- font-display: swap;
66
- size-adjust: 106.383%;
67
- }
68
-
69
- @font-face {
70
- font-family: 'Courier Prime';
71
- font-style: italic;
72
- font-weight: 400;
73
- src: url('courier-prime/CourierPrime-Italic.woff2') format('woff2');
74
- font-display: swap;
75
- size-adjust: 106.383%;
76
- }
77
-
78
- @font-face {
79
- font-family: 'Courier Prime';
80
- font-style: normal;
81
- font-weight: 700;
82
- src: url('courier-prime/CourierPrime-Bold.woff2') format('woff2');
83
- font-display: swap;
84
- size-adjust: 106.383%;
85
- }
86
-
87
- @font-face {
88
- font-family: 'Courier Prime';
89
- font-style: italic;
90
- font-weight: 700;
91
- src: url('courier-prime/CourierPrime-BoldItalic.woff2') format('woff2');
92
- font-display: swap;
93
- size-adjust: 106.383%;
94
- }
95
-
96
- ::selection {
97
- color: var(--bg-color);
98
- background: var(--fg-color);
99
- }
100
-
101
- :focus-visible {
102
- outline: 2px solid var(--theme-color);
103
- outline-offset: 1px;
104
- }
105
-
106
- a.disabled {
107
- color: var(--fg-color-translucent);
108
- text-decoration-color: var(--fg-color-translucent);
109
- cursor: not-allowed;
110
-
111
- @media (hover: hover) {
112
- &:hover {
113
- color: var(--fg-color-translucent);
114
- text-decoration-line: none;
115
- }
116
- }
117
- }
118
-
119
- .external-link-tail {
120
- white-space: nowrap;
121
- }
122
-
123
- .external-link-tail::after,
124
- a.external:not(:has(.external-link-tail))::after {
125
- vertical-align: text-top;
126
-
127
- @include contained-mask(var(--icon-external-link));
128
- }
129
-
130
- .external-link-tail::after {
131
- display: inline;
132
-
133
- // With zero font metrics, block-end padding paints down from text-top.
134
- padding-block-end: var(--icon-external-link-size);
135
- padding-inline-start: var(--icon-external-link-size);
136
- font-size: 0;
137
- line-height: 0;
138
- content: '\2060' / '';
139
- }
140
-
141
- a.external:not(:has(.external-link-tail))::after {
142
- display: inline-block;
143
- width: var(--icon-external-link-size);
144
- height: var(--icon-external-link-size);
145
- content: '';
146
- }
147
-
148
- .footnotes {
149
- padding-top: 32px;
150
- margin-top: calc(var(--thematic-break-margin) / 2);
151
- font-size: 0.9em;
152
- border-top: 3px solid var(--bg2-color);
153
- }
154
-
155
- .footnotes .title {
156
- @include user-select(none);
157
-
158
- font-size: larger;
159
- font-weight: 700;
160
- }
161
-
162
- .footnotes p {
163
- margin: 0;
164
- }
165
-
166
- .footnotes li {
167
- margin-bottom: 8px;
168
- }
169
-
170
- a.footnote-ref {
171
- font-size: 0.8em;
172
- vertical-align: super;
173
- border-bottom: none;
174
- }
175
-
176
- .footnote-item {
177
- &:target {
178
- outline: 2px solid var(--theme-color);
179
- outline-offset: 1px;
180
- }
181
- }
182
-
183
- a.footnote-ref,
184
- .footnotes .footnote-backref {
185
- display: inline-block;
186
-
187
- @include user-select(none);
188
- }
189
-
190
- hr {
191
- display: flex;
192
- align-items: center;
193
- justify-content: center;
194
- height: 32px;
195
- margin: var(--section-padding) 0;
196
- border: none;
197
- }
198
-
199
- hr::after {
200
- display: block;
201
- width: 160px;
202
- height: 12px;
203
- content: '';
204
- background-color: var(--fg2-color);
205
- mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 160 12'><rect x='0' y='5.5' width='96' height='1' fill='black'/><path d='M 64,6 C 71,6 80,4.5 80,1 C 80,4.5 89,6 96,6 C 89,6 80,7.5 80,11 C 80,7.5 71,6 64,6 Z' fill='black'/><rect x='64' y='5.5' width='96' height='1' fill='black'/></svg>");
206
- mask-repeat: no-repeat;
207
- mask-size: 100% 100%;
208
- }
209
-
210
- button,
211
- a.button,
212
- input[type='submit'] {
213
- padding: 0.25em 0.66em;
214
- font-family: var(--font);
215
- font-size: var(--font-size-small);
216
- font-weight: 500;
217
- color: var(--fg-color);
218
- text-decoration: none;
219
- background: var(--bg-color);
220
- border: var(--control-border-width) solid var(--fg2-color);
221
- border-radius: var(--border-radius);
222
- }
223
-
224
- button:disabled,
225
- input[type='submit']:disabled {
226
- cursor: not-allowed;
227
- opacity: 0.4;
228
- }
229
-
230
- button:active:not(:disabled),
231
- a.button:not(.disabled):active,
232
- input[type='submit']:active:not(:disabled) {
233
- color: var(--bg-color) !important;
234
- background: var(--link-active-color) !important;
235
- }
236
-
237
- @media (hover: hover) {
238
- button:hover:not(:disabled),
239
- a.button:not(.disabled):hover,
240
- input[type='submit']:hover:not(:disabled) {
241
- cursor: pointer;
242
- background: var(--bg2-color);
243
- }
244
- }
245
-
246
- button:focus,
247
- a.button:focus,
248
- input[type='submit']:focus,
249
- input[type='text']:focus,
250
- input[type='search']:focus,
251
- select:focus {
252
- outline: 2px solid var(--theme-color);
253
- outline-offset: 1px;
254
- background-color: var(--bg2-color);
255
- border-color: var(--fg-color);
256
- }
257
-
258
- button.icon-button {
259
- display: inline-flex;
260
- align-items: center;
261
- justify-content: center;
262
- width: 30px;
263
- height: 30px;
264
- padding: 0;
265
- font-style: normal;
266
- font-weight: 400;
267
- vertical-align: top;
268
- border: none;
269
- border-radius: 999px;
270
- }
271
-
272
- button.icon-button:focus {
273
- background-color: var(--bg2-color);
274
- }
275
-
276
- ol.styled-list,
277
- ul.styled-list {
278
- font-weight: 700;
279
- color: var(--theme-color-text);
280
- }
281
-
282
- ul.styled-list {
283
- // Make bullet points slightly bigger
284
- font-size: 1.2em;
285
- }
286
-
287
- ol li .styled-list-item {
288
- font-size: 1em;
289
- font-weight: var(--font-weight);
290
- color: var(--fg-color);
291
- }
292
-
293
- ul li .styled-list-item {
294
- font-size: 0.8333em;
295
- font-weight: var(--font-weight);
296
- color: var(--fg-color);
297
- }
298
-
299
- ul {
300
- list-style-type: square;
301
- }
302
-
303
- ul ul {
304
- list-style-type: disc;
305
- }
306
-
307
- ul ul ul {
308
- list-style-type: circle;
309
- }
310
-
311
- ul ul ul ul {
312
- list-style-type: square;
313
- }
314
-
315
- ol {
316
- list-style-type: decimal;
317
- }
318
-
319
- :root[data-font-preference='serif'] ol > li::marker {
320
- font-variant-numeric: oldstyle-nums;
321
- }
322
-
323
- ol ol {
324
- list-style-type: lower-alpha;
325
- }
326
-
327
- ol ol ol {
328
- list-style-type: lower-roman;
329
- }
330
-
331
- ol ol ol ol {
332
- list-style-type: decimal;
333
- }
334
-
335
- ol ol ol ol ol {
336
- list-style-type: lower-alpha;
337
- }
338
-
339
- table {
340
- margin: 1rem 0;
341
- overflow: scroll;
342
- font-size: var(--font-size-smaller);
343
- border-collapse: collapse;
344
- }
345
-
346
- thead {
347
- text-align: left;
348
- }
349
-
350
- th,
351
- td {
352
- border-bottom: 1px solid var(--fg-color);
353
- }
354
-
355
- th {
356
- font-weight: 600;
357
- color: var(--theme-color-text);
358
- }
359
-
360
- th,
361
- td {
362
- padding: var(--table-padding) var(--table-padding) var(--table-padding) 0;
363
- vertical-align: top;
364
- text-align: left;
365
- }
366
-
367
- tbody tr:last-child td {
368
- border-bottom: none;
369
- }
370
-
371
- section {
372
- padding: calc(5 * var(--gap)) calc(2 * var(--gap));
373
- margin: 0 calc(-1 * var(--gap)) 2rem;
374
- background: linear-gradient(
375
- 0deg,
376
- transparent 0,
377
- var(--bg2-color) 3rem,
378
- var(--bg2-color) calc(100% - 3rem),
379
- transparent 100%
380
- );
381
-
382
- .alert {
383
- background: var(--bg-color);
384
- }
385
- }
386
-
387
- section h2:first-child,
388
- section h3:first-child,
389
- section h4:first-child,
390
- section h5:first-child,
391
- section h6:first-child {
392
- font-style: italic;
393
- font-weight: 600;
394
- }
395
-
396
- section > p:last-child,
397
- section > div:last-child {
398
- margin-bottom: 0;
399
- }
400
-
401
- section > h2 + p {
402
- margin-top: 16px;
403
- }
404
-
405
- blockquote {
406
- font-size: 1.2em;
407
- font-style: italic;
408
- color: var(--fg-color);
409
- }
410
-
411
- .heading-subtitle {
412
- font-style: italic;
413
- }
414
-
415
- h1 .heading-subtitle,
416
- h2 .heading-subtitle,
417
- h3 .heading-subtitle,
418
- h4 .heading-subtitle,
419
- h5 .heading-subtitle,
420
- h6 .heading-subtitle {
421
- margin-left: 1rem;
422
- font-weight: 400;
423
- }
424
-
425
- h3.has-subtitle,
426
- h4.has-subtitle,
427
- h5.has-subtitle,
428
- h6.has-subtitle {
429
- padding-bottom: 0.5em;
430
- margin-top: 6rem;
431
- margin-bottom: 0.5em;
432
- border-bottom: 1px solid var(--fg-color);
433
- }
434
-
435
- .small-caps {
436
- margin-right: -0.05em;
437
- font-variant-caps: all-small-caps;
438
- font-synthesis-small-caps: none;
439
- text-transform: uppercase;
440
- letter-spacing: 0.05em;
441
- }
442
-
443
- dl {
444
- dt {
445
- font-weight: 600;
446
- color: var(--theme-color-text);
447
- }
448
-
449
- dd > *:first-child {
450
- margin-top: 0;
451
- }
452
- }
453
-
454
- dfn {
455
- font-style: italic;
456
- font-weight: 500;
457
- color: var(--theme-color-text);
458
- }
459
-
460
- a.breadcrumb {
461
- @include user-select(none);
462
- @include subdued-link;
463
-
464
- &::after {
465
- display: inline-block;
466
- width: var(--icon-parent-size);
467
- height: var(--icon-parent-size);
468
- vertical-align: middle;
469
- content: '';
470
-
471
- @include contained-mask(var(--icon-parent));
472
- }
473
- }
474
-
475
- img {
476
- max-width: 100%;
477
- }
478
-
479
- .avatar {
480
- max-width: 100px;
481
- aspect-ratio: 1 / 1;
482
- object-fit: cover;
483
- border-radius: 999px;
484
- }
485
-
486
- figure.bio {
487
- float: left;
488
- max-width: 125px;
489
- margin: 1rem 1rem 1rem 0;
490
-
491
- @media (width > 540px) {
492
- max-width: 200px;
493
- }
494
-
495
- .avatar {
496
- display: block;
497
- width: 100%;
498
- max-width: 100%;
499
- }
500
-
501
- figcaption {
502
- margin-top: var(--gap);
503
- font-style: italic;
504
- text-align: center;
505
- }
506
- }
507
-
508
- kbd {
509
- display: inline-block;
510
- padding: 1px 5px;
511
- font-family: var(--font);
512
- font-size: var(--font-size-small);
513
- font-weight: 500;
514
- border: 1px solid var(--fg-color);
515
- border-radius: 4px;
516
- box-shadow: 0 2px 0 var(--fg-color);
517
- }
518
-
519
- select {
520
- position: relative;
521
- padding: 4px;
522
- font-family: var(--font);
523
- font-size: inherit;
524
- color: var(--fg-color);
525
- background: var(--bg-color);
526
- border: var(--control-border-width) solid var(--fg2-color);
527
- border-radius: var(--border-radius);
528
- }
529
-
530
- // Shiki dual-theme support (defaultColor: false, CSS custom properties only,
531
- // no inline color/background-color, so no !important needed)
532
- .shiki span {
533
- color: var(--shiki-light);
534
- }
535
-
536
- @media (prefers-color-scheme: dark) {
537
- .shiki span {
538
- color: var(--shiki-dark);
539
- }
540
- }
12
+ @use 'fonts';
13
+ @use 'content';
14
+ @use 'material-symbols';
15
+ @use 'anchor';
16
+ @use 'code';
17
+ @use 'literate';
18
+ @use 'header';
19
+ @use 'print';
20
+ @use 'question';
21
+ @use 'search';
22
+ @use 'slides';
23
+ @use 'timezone';
24
+ @use 'toc';
25
+ @use 'trace';
26
+ @use 'appearance-picker';
27
+ @use 'navigate';
28
+ @use 'page-update';
@@ -4,6 +4,7 @@ import {
4
4
  normalizeHM,
5
5
  to12Hour,
6
6
  to12HourMarkup,
7
+ to24Hour,
7
8
  type PeriodStyle,
8
9
  } from './time-format';
9
10
 
@@ -149,6 +150,23 @@ export default (window: Window) => {
149
150
  periodStyles.set(el, detectPeriodStyle(el.textContent ?? ''));
150
151
  });
151
152
  const pagePeriodStyle = dominantPeriodStyle(periodStyles);
153
+ const twentyFourHourTimes = new Set<HTMLTimeElement>();
154
+ for (const [el, style] of periodStyles) {
155
+ const rangeEnd = getRangeEnd(el);
156
+ const inheritsPeriod =
157
+ rangeEnd !== null && periodStyles.get(rangeEnd) != null;
158
+ const authoredTime = (el.textContent ?? '').trim();
159
+ // A periodless 12-hour prefix may inherit its range's final AM/PM.
160
+ // Otherwise text matching datetime is an authored 24-hour time.
161
+ if (
162
+ style === null &&
163
+ !inheritsPeriod &&
164
+ /^\d{1,2}:\d{2}$/.test(authoredTime) &&
165
+ parseHHMM(authoredTime) === parseHHMM(el.getAttribute('datetime') ?? '')
166
+ ) {
167
+ twentyFourHourTimes.add(el);
168
+ }
169
+ }
152
170
 
153
171
  function updateTimes(targetTz: string) {
154
172
  const target = timezones.find(t => t.value === targetTz) || defaultTz;
@@ -187,11 +205,12 @@ export default (window: Window) => {
187
205
  }
188
206
  }
189
207
 
208
+ const is24Hour = twentyFourHourTimes.has(el);
190
209
  const originalStyle = periodStyles.get(el) ?? null;
191
210
  const originalIsPm = Math.floor(baseMinutes / 60) >= 12;
192
211
  const periodChanged = originalIsPm !== h >= 12 || dayShift !== 0;
193
212
  let style =
194
- originalStyle === null && periodChanged
213
+ !is24Hour && originalStyle === null && periodChanged
195
214
  ? pagePeriodStyle
196
215
  : originalStyle;
197
216
 
@@ -201,6 +220,7 @@ export default (window: Window) => {
201
220
  ? parseHHMM(rangeEndDatetime)
202
221
  : NaN;
203
222
  if (
223
+ !is24Hour &&
204
224
  originalStyle === null &&
205
225
  rangeEnd !== null &&
206
226
  !isNaN(rangeEndMinutes)
@@ -216,14 +236,20 @@ export default (window: Window) => {
216
236
  isDefault || style === null || rangeEnd !== null
217
237
  ? ''
218
238
  : ` <span class="small-caps">${target.abbreviation}</span>`;
219
- el.innerHTML = to12HourMarkup(h, m, style) + timeZone + suffix;
239
+ el.innerHTML =
240
+ (is24Hour ? to24Hour(h, m) : to12HourMarkup(h, m, style)) +
241
+ timeZone +
242
+ suffix;
220
243
 
221
244
  if (isDefault) {
222
245
  el.classList.remove('is-modified');
223
246
  el.title = '';
224
247
  } else {
225
248
  el.classList.add('is-modified');
226
- el.title = `${to12Hour(...normalizeHM(baseMinutes), style ?? pagePeriodStyle)} ${defaultTz.abbreviation}`;
249
+ const originalTime = is24Hour
250
+ ? to24Hour(...normalizeHM(baseMinutes))
251
+ : to12Hour(...normalizeHM(baseMinutes), style ?? pagePeriodStyle);
252
+ el.title = `${originalTime} ${defaultTz.abbreviation}`;
227
253
  }
228
254
  });
229
255
  }
@@ -22,6 +22,10 @@ function pad(n: number | string) {
22
22
  return String(n).padStart(2, '0');
23
23
  }
24
24
 
25
+ export function to24Hour(h: number, m: number) {
26
+ return `${pad(h)}:${pad(m)}`;
27
+ }
28
+
25
29
  export function to12Hour(
26
30
  h: number,
27
31
  m: number,
package/src/toc/index.ts CHANGED
@@ -45,7 +45,7 @@ function getHeadingsAndAlerts(
45
45
  ): (HTMLHeadingElement | HTMLDivElement)[] {
46
46
  return Array.from(
47
47
  parent.querySelectorAll(
48
- '.body h1, .body h2, .body h3, .body h4, .body h5, .body h6, .body > div.alert, .body section > div.alert',
48
+ '.body h1, .body h2, .body h3, .body h4, .body h5, .body h6, :is(.body, .page-content) > div.alert, .body section > div.alert',
49
49
  ),
50
50
  );
51
51
  }
@@ -55,7 +55,7 @@ function getTocElements(
55
55
  ): (HTMLHeadingElement | HTMLDivElement | HTMLHRElement)[] {
56
56
  return Array.from(
57
57
  parent.querySelectorAll(
58
- '.body h1, .body h2, .body h3, .body h4, .body h5, .body h6, .body > div.alert, .body section > div.alert, .body > hr',
58
+ '.body h1, .body h2, .body h3, .body h4, .body h5, .body h6, :is(.body, .page-content) > div.alert, .body section > div.alert, :is(.body, .page-content) > hr',
59
59
  ),
60
60
  );
61
61
  }
@@ -123,11 +123,12 @@ export default (window: Window) => {
123
123
  }
124
124
 
125
125
  let best = 0;
126
+ let bestLine = -1;
127
+ // TOC display groups (such as fields and methods) are not source order.
126
128
  for (let i = 0; i < codeLines.length; i++) {
127
- if (codeLines[i] <= line) {
129
+ if (codeLines[i] <= line && codeLines[i] >= bestLine) {
128
130
  best = i;
129
- } else {
130
- break;
131
+ bestLine = codeLines[i];
131
132
  }
132
133
  }
133
134