rshow 0.0.4 → 0.0.6
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.
- package/dist/main.js +344 -6
- package/package.json +1 -1
- package/skills/rshow/SKILL.md +10 -4
- package/skills/rshow/reference/dashboard.md +1 -1
- package/skills/rshow/reference/mockups.md +7 -4
- package/skills/rshow/reference/pages.md +6 -4
- package/skills/rshow/reference/poster.md +1 -1
- package/skills/rshow/reference/project-page.md +1 -1
- package/skills/rshow/reference/sketch.md +1 -1
- package/skills/rshow-slides/SKILL.md +17 -8
- package/skills/rshow-slides/reference/layouts.md +50 -0
- package/themes/default/deck.css +325 -1
- package/themes/default/deck.js +169 -1
- package/themes/default/index.html +5 -3
- package/themes/default/mockup.css +105 -2
- package/themes/default/mockup.js +342 -18
- package/themes/default/terminal.css +8 -0
- package/themes/default/terminal.js +70 -13
- package/worker/index.js +15 -15
|
@@ -14,9 +14,11 @@
|
|
|
14
14
|
- two-col
|
|
15
15
|
- quote
|
|
16
16
|
- big-number
|
|
17
|
+
- your own layout
|
|
17
18
|
|
|
18
19
|
One markup example per layout, all inside `<main class="deck">`.
|
|
19
20
|
Every slide carries `slide` plus one layout class, and the layout class goes second.
|
|
21
|
+
The twelve are a starting set: a deck may add a layout of its own when none of them carries the claim.
|
|
20
22
|
Nothing sits above a title: no kicker, no label, no number.
|
|
21
23
|
|
|
22
24
|
A sparse slide (`title`, `section`, `statement`, `quote`, `big-number`, `bullets`) is centred a little above the middle of the 1280x720 canvas, so the air sits under the block instead of closing it in.
|
|
@@ -249,3 +251,51 @@ The unit belongs in the statement and the source goes under it.
|
|
|
249
251
|
```
|
|
250
252
|
|
|
251
253
|
A number you cannot measure is `[PLACEHOLDER]` until it is real, and a number that stands for an example says so in the source line.
|
|
254
|
+
|
|
255
|
+
## your own layout
|
|
256
|
+
|
|
257
|
+
A layout of your own takes a short class name and sits second on the slide, in the same slot as the twelve.
|
|
258
|
+
Pick the shape the claim needs, such as an annotated figure, a figure with a callout, a diagram built in HTML or SVG, a timeline, a before and after or a comparison grid.
|
|
259
|
+
Its CSS goes in one inline `<style>` block after the theme links, scoped to `.slide.<name>`, with colour, type and space from the tokens.
|
|
260
|
+
Here one figure carries three callouts, and they stack under it below 640px.
|
|
261
|
+
|
|
262
|
+
```html
|
|
263
|
+
<style>
|
|
264
|
+
.slide.annotated .annotated-fig { position: relative; margin: 0; }
|
|
265
|
+
.slide.annotated .annotated-fig img { display: block; width: 100%; }
|
|
266
|
+
.slide.annotated .callout {
|
|
267
|
+
position: absolute;
|
|
268
|
+
max-width: 30%;
|
|
269
|
+
padding: var(--space-03) var(--space-04);
|
|
270
|
+
background: var(--layer-01);
|
|
271
|
+
border: 1px solid var(--border-strong);
|
|
272
|
+
color: var(--fg);
|
|
273
|
+
font-size: 18px;
|
|
274
|
+
line-height: 1.4;
|
|
275
|
+
}
|
|
276
|
+
.slide.annotated .callout.one { top: 6%; left: 3%; }
|
|
277
|
+
.slide.annotated .callout.two { top: 40%; right: 3%; }
|
|
278
|
+
.slide.annotated .callout.three { bottom: 6%; left: 30%; }
|
|
279
|
+
@media (max-width: 640px) {
|
|
280
|
+
.slide.annotated .callout {
|
|
281
|
+
position: static;
|
|
282
|
+
max-width: none;
|
|
283
|
+
margin: var(--space-03) 0 0;
|
|
284
|
+
padding: 0;
|
|
285
|
+
border: 0;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
</style>
|
|
289
|
+
|
|
290
|
+
<section class="slide annotated">
|
|
291
|
+
<h2>The tail of the log is what a restart replays</h2>
|
|
292
|
+
<figure class="annotated-fig">
|
|
293
|
+
<img src="write-path.svg" alt="A log appended before the map is updated, with three stages marked.">
|
|
294
|
+
<p class="callout one">The append lands first.</p>
|
|
295
|
+
<p class="callout two">The map follows it.</p>
|
|
296
|
+
<p class="callout three">A crash replays the tail.</p>
|
|
297
|
+
<figcaption>A restart loses nothing after step 2.</figcaption>
|
|
298
|
+
</figure>
|
|
299
|
+
<aside class="notes"><p>Trace the three callouts in order.</p></aside>
|
|
300
|
+
</section>
|
|
301
|
+
```
|
package/themes/default/deck.css
CHANGED
|
@@ -517,6 +517,323 @@ aside.notes {
|
|
|
517
517
|
pointer-events: none;
|
|
518
518
|
}
|
|
519
519
|
|
|
520
|
+
/* --------------------------------------------------------------- reading */
|
|
521
|
+
|
|
522
|
+
/* A phone held upright cannot show a 16:9 slide: 390px of width leaves a 219px
|
|
523
|
+
strip. So the deck stops presenting and reads. Every slide becomes a block
|
|
524
|
+
down the page, the width of the screen, one after another, with no scale in
|
|
525
|
+
it: a 1280px canvas shrunk to 390px sets the body text near 9px, which is a
|
|
526
|
+
picture of a slide and not a slide. The type takes sizes a phone reads, each
|
|
527
|
+
figure takes the width it is given, a wide table or a block of code scrolls
|
|
528
|
+
sideways inside itself, and every grid stacks to one column. The notes stay
|
|
529
|
+
hidden, every step is out, and a hairline rule with an "n / total" label in
|
|
530
|
+
the margin marks where one slide ends and the next begins.
|
|
531
|
+
deck.js turns this on for a viewport that is portrait and narrower than 700px,
|
|
532
|
+
and off everywhere else: a landscape phone and a desktop keep one slide at a
|
|
533
|
+
time. The button that goes back to presenting is deck.js's, and Esc comes back
|
|
534
|
+
here. It is a screen mode, so it sits in @media screen: a sheet of paper keeps
|
|
535
|
+
one slide per page, laid out on the canvas it was drawn for. */
|
|
536
|
+
|
|
537
|
+
@media screen {
|
|
538
|
+
html.is-reading,
|
|
539
|
+
body.is-reading {
|
|
540
|
+
height: auto;
|
|
541
|
+
overflow: visible;
|
|
542
|
+
background: var(--bg);
|
|
543
|
+
/* Reading sizes. The type scale has no 17px step and the canvas headings are
|
|
544
|
+
drawn for 1280px, so a phone slide names the size of its own body text and
|
|
545
|
+
of the one line a quote or a statement sets. Every other size below is a
|
|
546
|
+
token: 32px, 24px, 20px, 18px, 14px. */
|
|
547
|
+
--deck-read-body: 17px;
|
|
548
|
+
--deck-read-line: 22px;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/* The deck is the page now: no canvas, no transform, no clip. The bottom pad
|
|
552
|
+
clears the Present button, so the last line of the last slide stays under
|
|
553
|
+
the reader's thumb and not inside it. */
|
|
554
|
+
main.deck.is-reading {
|
|
555
|
+
position: static;
|
|
556
|
+
width: auto;
|
|
557
|
+
height: auto;
|
|
558
|
+
margin: 0;
|
|
559
|
+
padding: 0 0 80px;
|
|
560
|
+
overflow: visible;
|
|
561
|
+
transform: none !important;
|
|
562
|
+
background: var(--bg);
|
|
563
|
+
counter-reset: deck-slide;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/* One slide, one block: the width of the screen, a 16px gutter either side,
|
|
567
|
+
air above and below, and a rule where the previous slide ended. */
|
|
568
|
+
main.deck.is-reading > .slide,
|
|
569
|
+
main.deck.is-reading.is-ready > .slide {
|
|
570
|
+
position: relative;
|
|
571
|
+
inset: auto;
|
|
572
|
+
display: block;
|
|
573
|
+
width: auto;
|
|
574
|
+
height: auto;
|
|
575
|
+
min-height: 0;
|
|
576
|
+
margin: 0;
|
|
577
|
+
padding: var(--space-06) var(--space-05);
|
|
578
|
+
overflow: visible;
|
|
579
|
+
opacity: 1;
|
|
580
|
+
visibility: visible;
|
|
581
|
+
transform: none;
|
|
582
|
+
border-top: 1px solid var(--border-subtle);
|
|
583
|
+
counter-increment: deck-slide;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
main.deck.is-reading > .slide:first-child {
|
|
587
|
+
border-top: 0;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/* Where this slide sits in the stack. It is a label, so sans, sentence case
|
|
591
|
+
and the muted colour of every other label: mono and caps are for code. */
|
|
592
|
+
main.deck.is-reading > .slide::before {
|
|
593
|
+
content: counter(deck-slide) " / " var(--deck-total, "");
|
|
594
|
+
display: block;
|
|
595
|
+
margin: 0 0 var(--space-05);
|
|
596
|
+
font-family: var(--font-sans);
|
|
597
|
+
font-size: var(--text-2xs);
|
|
598
|
+
font-weight: 400;
|
|
599
|
+
line-height: 1.33;
|
|
600
|
+
letter-spacing: 0;
|
|
601
|
+
text-transform: none;
|
|
602
|
+
color: var(--muted);
|
|
603
|
+
font-variant-numeric: tabular-nums;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/* ----------------------------------------------------- reading type --- */
|
|
607
|
+
|
|
608
|
+
/* The display steps belong to a 1280px slide: 76px of heading in 358px of
|
|
609
|
+
width is three words a line. In reading mode a heading is a heading. */
|
|
610
|
+
main.deck.is-reading .slide h1,
|
|
611
|
+
main.deck.is-reading .slide h2,
|
|
612
|
+
main.deck.is-reading .slide h3 {
|
|
613
|
+
margin: 0 0 var(--space-04);
|
|
614
|
+
max-width: none;
|
|
615
|
+
font-weight: 400;
|
|
616
|
+
line-height: var(--lh-heading);
|
|
617
|
+
letter-spacing: 0;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
main.deck.is-reading .slide h1 {
|
|
621
|
+
font-size: var(--text-xl);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
main.deck.is-reading .slide h2 {
|
|
625
|
+
font-size: var(--text-md);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
main.deck.is-reading .slide h3 {
|
|
629
|
+
font-size: var(--text-base);
|
|
630
|
+
font-weight: 600;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/* Running text: 17px on a 1.45 leading reads on a phone without a pinch, and
|
|
634
|
+
the measure is the screen, so nothing caps it short of the gutter. */
|
|
635
|
+
main.deck.is-reading .slide p,
|
|
636
|
+
main.deck.is-reading .slide li,
|
|
637
|
+
main.deck.is-reading .slide li > p,
|
|
638
|
+
main.deck.is-reading .slide .col p,
|
|
639
|
+
main.deck.is-reading .slide .col li {
|
|
640
|
+
max-width: none;
|
|
641
|
+
font-size: var(--deck-read-body);
|
|
642
|
+
line-height: var(--lh-body);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
main.deck.is-reading .slide ul,
|
|
646
|
+
main.deck.is-reading .slide ol {
|
|
647
|
+
padding-left: var(--space-06);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
main.deck.is-reading .slide li {
|
|
651
|
+
margin-bottom: var(--space-03);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
main.deck.is-reading .slide li:last-child {
|
|
655
|
+
margin-bottom: 0;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
main.deck.is-reading .slide.title .subtitle {
|
|
659
|
+
margin-top: var(--space-04);
|
|
660
|
+
max-width: none;
|
|
661
|
+
font-size: var(--text-sm);
|
|
662
|
+
line-height: 1.4;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/* The line under a title, a figure or a table: a caption, so small and muted,
|
|
666
|
+
and never wider than the slide. */
|
|
667
|
+
main.deck.is-reading .slide .muted,
|
|
668
|
+
main.deck.is-reading .slide figcaption,
|
|
669
|
+
main.deck.is-reading .slide cite,
|
|
670
|
+
main.deck.is-reading .slide caption,
|
|
671
|
+
main.deck.is-reading .slide .byline,
|
|
672
|
+
main.deck.is-reading .slide .source,
|
|
673
|
+
main.deck.is-reading .slide li > p {
|
|
674
|
+
max-width: none;
|
|
675
|
+
font-size: var(--text-2xs);
|
|
676
|
+
line-height: 1.45;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
main.deck.is-reading .slide.section h2 {
|
|
680
|
+
margin-bottom: 0;
|
|
681
|
+
font-size: var(--text-md);
|
|
682
|
+
font-weight: 400;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
main.deck.is-reading .slide.statement p,
|
|
686
|
+
main.deck.is-reading .slide.quote blockquote {
|
|
687
|
+
max-width: none;
|
|
688
|
+
font-size: var(--deck-read-line);
|
|
689
|
+
font-weight: 400;
|
|
690
|
+
line-height: 1.4;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
main.deck.is-reading .slide.big-number .value {
|
|
694
|
+
font-size: var(--text-2xl);
|
|
695
|
+
line-height: var(--lh-flat);
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
main.deck.is-reading .slide.big-number .caption {
|
|
699
|
+
max-width: none;
|
|
700
|
+
font-size: var(--text-sm);
|
|
701
|
+
line-height: 1.4;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/* A statement or a divider carries one short line, so it keeps a little
|
|
705
|
+
height and sits its text in the middle of it instead of at the top. */
|
|
706
|
+
main.deck.is-reading > .slide.statement,
|
|
707
|
+
main.deck.is-reading > .slide.section {
|
|
708
|
+
display: flex;
|
|
709
|
+
flex-direction: column;
|
|
710
|
+
justify-content: center;
|
|
711
|
+
min-height: 40vh;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
/* ------------------------------------------------ reading containers -- */
|
|
715
|
+
|
|
716
|
+
/* Anything that holds a picture takes the width it is given, keeps its own
|
|
717
|
+
shape, and never reaches past the gutter. */
|
|
718
|
+
main.deck.is-reading .slide figure {
|
|
719
|
+
display: block;
|
|
720
|
+
margin: 0;
|
|
721
|
+
min-height: 0;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
main.deck.is-reading .slide img,
|
|
725
|
+
main.deck.is-reading .slide svg,
|
|
726
|
+
main.deck.is-reading .slide video,
|
|
727
|
+
main.deck.is-reading .slide canvas {
|
|
728
|
+
display: block;
|
|
729
|
+
max-width: 100%;
|
|
730
|
+
height: auto;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
/* A table keeps its columns and a code block keeps its lines: each one
|
|
734
|
+
scrolls sideways inside itself rather than widening the page. */
|
|
735
|
+
main.deck.is-reading .slide table {
|
|
736
|
+
display: block;
|
|
737
|
+
width: 100%;
|
|
738
|
+
max-width: 100%;
|
|
739
|
+
overflow-x: auto;
|
|
740
|
+
font-size: 15px;
|
|
741
|
+
line-height: 1.4;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
main.deck.is-reading .slide th,
|
|
745
|
+
main.deck.is-reading .slide td {
|
|
746
|
+
padding: var(--space-03) var(--space-04);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
main.deck.is-reading .slide pre {
|
|
750
|
+
max-width: 100%;
|
|
751
|
+
max-height: none;
|
|
752
|
+
padding: var(--space-05);
|
|
753
|
+
overflow-x: auto;
|
|
754
|
+
overflow-y: hidden;
|
|
755
|
+
font-size: 14px;
|
|
756
|
+
line-height: var(--lh-code);
|
|
757
|
+
white-space: pre;
|
|
758
|
+
word-break: normal;
|
|
759
|
+
tab-size: 2;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
/* Two columns and two figures side by side are a landscape idea. On a phone
|
|
763
|
+
every grid is one column, in the order the deck wrote it. */
|
|
764
|
+
main.deck.is-reading > .slide.two-col,
|
|
765
|
+
main.deck.is-reading > .slide.three-col,
|
|
766
|
+
main.deck.is-reading > .slide .pair,
|
|
767
|
+
main.deck.is-reading > .slide .col,
|
|
768
|
+
main.deck.is-reading > .slide .cols,
|
|
769
|
+
main.deck.is-reading > .slide .grid,
|
|
770
|
+
main.deck.is-reading > .slide .gallery {
|
|
771
|
+
display: block;
|
|
772
|
+
grid-template-columns: none;
|
|
773
|
+
gap: 0;
|
|
774
|
+
min-height: 0;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
main.deck.is-reading .slide.two-col > h2 {
|
|
778
|
+
grid-column: auto;
|
|
779
|
+
margin-bottom: var(--space-05);
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
main.deck.is-reading .slide .pair > figure + figure,
|
|
783
|
+
main.deck.is-reading .slide .col + .col {
|
|
784
|
+
margin-top: var(--space-06);
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
/* --------------------------------------------------- reading chrome --- */
|
|
788
|
+
|
|
789
|
+
/* A reader scrolls. A keypress it cannot give, so nothing waits for one, and
|
|
790
|
+
the notes and the counter are a presenter's furniture, not a reader's. */
|
|
791
|
+
main.deck.is-reading .step {
|
|
792
|
+
opacity: 1;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
main.deck.is-reading aside.notes,
|
|
796
|
+
main.deck.is-reading .deck-hud {
|
|
797
|
+
display: none;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
/* One slide at a time, for a room or for a phone turned back to presenting. */
|
|
802
|
+
.deck-present {
|
|
803
|
+
position: fixed;
|
|
804
|
+
z-index: 20;
|
|
805
|
+
right: max(16px, env(safe-area-inset-right, 16px));
|
|
806
|
+
bottom: max(16px, env(safe-area-inset-bottom, 16px));
|
|
807
|
+
min-width: 44px;
|
|
808
|
+
min-height: 44px;
|
|
809
|
+
padding: 0 14px;
|
|
810
|
+
font-family: var(--font-sans);
|
|
811
|
+
font-size: var(--text-2xs);
|
|
812
|
+
line-height: 1;
|
|
813
|
+
letter-spacing: 0;
|
|
814
|
+
color: var(--fg);
|
|
815
|
+
background: var(--bg);
|
|
816
|
+
border: 1px solid var(--border-subtle);
|
|
817
|
+
border-radius: var(--radius);
|
|
818
|
+
cursor: pointer;
|
|
819
|
+
-webkit-appearance: none;
|
|
820
|
+
appearance: none;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
.deck-present[hidden] {
|
|
824
|
+
display: none;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
/* A phone in present mode keeps the canvas on the window rather than in the
|
|
828
|
+
document: a 1280px box in the page widens a phone's layout viewport and
|
|
829
|
+
pushes the slide off the screen. */
|
|
830
|
+
@media (max-width: 699px) {
|
|
831
|
+
main.deck {
|
|
832
|
+
position: fixed;
|
|
833
|
+
inset: 0;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
520
837
|
@media (prefers-reduced-motion: reduce) {
|
|
521
838
|
.step {
|
|
522
839
|
transition: none;
|
|
@@ -574,9 +891,16 @@ aside.notes {
|
|
|
574
891
|
}
|
|
575
892
|
|
|
576
893
|
.deck-hud,
|
|
577
|
-
aside.notes
|
|
894
|
+
aside.notes,
|
|
895
|
+
.deck-present {
|
|
578
896
|
display: none !important;
|
|
579
897
|
}
|
|
898
|
+
|
|
899
|
+
/* Reading mode is a screen mode, so a page printed from a phone is the deck
|
|
900
|
+
on its canvas again: one slide, one sheet. Nothing here has to undo it. */
|
|
901
|
+
main.deck.is-reading {
|
|
902
|
+
overflow: visible;
|
|
903
|
+
}
|
|
580
904
|
}
|
|
581
905
|
|
|
582
906
|
/* Exact 16:9 sheet for one slide per page. */
|
package/themes/default/deck.js
CHANGED
|
@@ -309,6 +309,9 @@
|
|
|
309
309
|
writeHash();
|
|
310
310
|
syncNotes();
|
|
311
311
|
syncPresenter();
|
|
312
|
+
if (reading) {
|
|
313
|
+
scrollToCurrent();
|
|
314
|
+
}
|
|
312
315
|
}
|
|
313
316
|
|
|
314
317
|
function go(n) {
|
|
@@ -619,6 +622,14 @@
|
|
|
619
622
|
cycleMode();
|
|
620
623
|
event.preventDefault();
|
|
621
624
|
break;
|
|
625
|
+
case 'Escape':
|
|
626
|
+
// Out of one slide at a time, back to the stack the phone reads.
|
|
627
|
+
if (reading || forced !== null) {
|
|
628
|
+
forced = null;
|
|
629
|
+
setReading(wantsReading());
|
|
630
|
+
event.preventDefault();
|
|
631
|
+
}
|
|
632
|
+
break;
|
|
622
633
|
default:
|
|
623
634
|
break;
|
|
624
635
|
}
|
|
@@ -673,6 +684,147 @@
|
|
|
673
684
|
}
|
|
674
685
|
}, { passive: true });
|
|
675
686
|
|
|
687
|
+
// ------------------------------------------------------------- reading --
|
|
688
|
+
|
|
689
|
+
// A phone held upright has no room for a 1280 x 720 canvas: the slide arrives
|
|
690
|
+
// as a 390 x 219 strip. deck.css stacks every slide down the page as a block
|
|
691
|
+
// at the width of the screen and reflows its type, so there is no scale to
|
|
692
|
+
// work out here; this is the part that decides when, and the small fixed
|
|
693
|
+
// button and Esc are how a reader switches.
|
|
694
|
+
var READING = window.matchMedia
|
|
695
|
+
? window.matchMedia('(max-width: 699px) and (orientation: portrait)')
|
|
696
|
+
: null;
|
|
697
|
+
var reading = false;
|
|
698
|
+
var forced = null; // 'read', 'present', or null for whatever the device is
|
|
699
|
+
var toggleButton = null;
|
|
700
|
+
|
|
701
|
+
function wantsReading() {
|
|
702
|
+
if (forced === 'read') {
|
|
703
|
+
return true;
|
|
704
|
+
}
|
|
705
|
+
if (forced === 'present') {
|
|
706
|
+
return false;
|
|
707
|
+
}
|
|
708
|
+
return READING !== null && READING.matches;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
function scrollToCurrent() {
|
|
712
|
+
if (!slides[current]) {
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
var box = slides[current].getBoundingClientRect();
|
|
716
|
+
var top = (window.pageYOffset || document.documentElement.scrollTop || 0) + box.top;
|
|
717
|
+
window.scrollTo(0, top);
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
// Scrolling the stack is navigating it: the slide at the top of the window is
|
|
721
|
+
// the current one, so the counter and the URL follow the reader.
|
|
722
|
+
function followScroll() {
|
|
723
|
+
if (!reading) {
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
var top = window.pageYOffset || document.documentElement.scrollTop || 0;
|
|
727
|
+
var at = 0;
|
|
728
|
+
var k;
|
|
729
|
+
var box;
|
|
730
|
+
for (k = 0; k < slides.length; k += 1) {
|
|
731
|
+
box = slides[k].getBoundingClientRect();
|
|
732
|
+
if (box.top <= 4) {
|
|
733
|
+
at = k;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
if (top + window.innerHeight >= document.documentElement.scrollHeight - 4) {
|
|
737
|
+
at = slides.length - 1;
|
|
738
|
+
}
|
|
739
|
+
if (at === current) {
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
current = at;
|
|
743
|
+
syncHud();
|
|
744
|
+
writeHash();
|
|
745
|
+
syncNotes();
|
|
746
|
+
syncPresenter();
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
function syncToggle() {
|
|
750
|
+
if (!toggleButton) {
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
toggleButton.hidden = !reading && forced !== 'present';
|
|
754
|
+
toggleButton.textContent = reading ? 'Present' : 'Read';
|
|
755
|
+
toggleButton.setAttribute('aria-label', reading
|
|
756
|
+
? 'Present: one slide at a time, Esc comes back to reading'
|
|
757
|
+
: 'Read: every slide down the page, Esc comes back to presenting');
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
function setReading(on) {
|
|
761
|
+
reading = on;
|
|
762
|
+
deck.classList.toggle('is-reading', on);
|
|
763
|
+
document.documentElement.classList.toggle('is-reading', on);
|
|
764
|
+
document.body.classList.toggle('is-reading', on);
|
|
765
|
+
syncToggle();
|
|
766
|
+
if (on) {
|
|
767
|
+
var k;
|
|
768
|
+
// The reader sees the whole deck, so no step waits for a key.
|
|
769
|
+
for (k = 0; k < slides.length; k += 1) {
|
|
770
|
+
revealed[k] = stepsOf(k).length;
|
|
771
|
+
}
|
|
772
|
+
render();
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
fit();
|
|
776
|
+
render();
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
function flip(which) {
|
|
780
|
+
forced = which;
|
|
781
|
+
setReading(wantsReading());
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
function mountToggle() {
|
|
785
|
+
toggleButton = document.createElement('button');
|
|
786
|
+
toggleButton.type = 'button';
|
|
787
|
+
toggleButton.className = 'deck-present';
|
|
788
|
+
toggleButton.addEventListener('click', function () {
|
|
789
|
+
flip(reading ? 'present' : 'read');
|
|
790
|
+
});
|
|
791
|
+
document.body.appendChild(toggleButton);
|
|
792
|
+
syncToggle();
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
if (READING !== null) {
|
|
796
|
+
if (READING.addEventListener) {
|
|
797
|
+
READING.addEventListener('change', function () {
|
|
798
|
+
if (forced === null) {
|
|
799
|
+
setReading(wantsReading());
|
|
800
|
+
}
|
|
801
|
+
});
|
|
802
|
+
} else if (READING.addListener) {
|
|
803
|
+
READING.addListener(function () {
|
|
804
|
+
if (forced === null) {
|
|
805
|
+
setReading(wantsReading());
|
|
806
|
+
}
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
var followQueued = false;
|
|
812
|
+
window.addEventListener('scroll', function () {
|
|
813
|
+
if (!reading || followQueued) {
|
|
814
|
+
return;
|
|
815
|
+
}
|
|
816
|
+
followQueued = true;
|
|
817
|
+
var run = function () {
|
|
818
|
+
followQueued = false;
|
|
819
|
+
followScroll();
|
|
820
|
+
};
|
|
821
|
+
if (window.requestAnimationFrame) {
|
|
822
|
+
window.requestAnimationFrame(run);
|
|
823
|
+
} else {
|
|
824
|
+
run();
|
|
825
|
+
}
|
|
826
|
+
}, { passive: true });
|
|
827
|
+
|
|
676
828
|
var resizeQueued = false;
|
|
677
829
|
function onResize() {
|
|
678
830
|
if (resizeQueued) {
|
|
@@ -681,7 +833,15 @@
|
|
|
681
833
|
resizeQueued = true;
|
|
682
834
|
var run = function () {
|
|
683
835
|
resizeQueued = false;
|
|
684
|
-
|
|
836
|
+
// A turn of the phone crosses the reading query: follow it, then lay the
|
|
837
|
+
// deck out for the mode it landed in.
|
|
838
|
+
if (forced === null && wantsReading() !== reading) {
|
|
839
|
+
setReading(wantsReading());
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
if (!reading) {
|
|
843
|
+
fit();
|
|
844
|
+
}
|
|
685
845
|
};
|
|
686
846
|
if (window.requestAnimationFrame) {
|
|
687
847
|
window.requestAnimationFrame(run);
|
|
@@ -715,6 +875,11 @@
|
|
|
715
875
|
deck.appendChild(hud);
|
|
716
876
|
}
|
|
717
877
|
hud.setAttribute('aria-hidden', 'true');
|
|
878
|
+
// Reading mode numbers each slide "n / total", and CSS cannot count the
|
|
879
|
+
// deck, so the count is a property the stack reads. content takes a string,
|
|
880
|
+
// so the value carries its quotes and the label prints 3 and not a bare
|
|
881
|
+
// number the counter cannot join.
|
|
882
|
+
deck.style.setProperty('--deck-total', '"' + slides.length + '"');
|
|
718
883
|
|
|
719
884
|
var match = /^#(\d+)$/.exec(window.location.hash);
|
|
720
885
|
current = match ? clamp(parseInt(match[1], 10) - 1) : 0;
|
|
@@ -723,6 +888,9 @@
|
|
|
723
888
|
|
|
724
889
|
fit();
|
|
725
890
|
render();
|
|
891
|
+
|
|
892
|
+
mountToggle();
|
|
893
|
+
setReading(wantsReading());
|
|
726
894
|
}
|
|
727
895
|
|
|
728
896
|
window.rshowDeck = {
|
|
@@ -409,9 +409,11 @@
|
|
|
409
409
|
<p>Every layout the theme ships, one frame each. A frame is lazy loaded, so
|
|
410
410
|
the page stays light on a phone, and the link under it opens the same example
|
|
411
411
|
full size. The deck, the page, the report, the project page, the poster and
|
|
412
|
-
the dashboard are
|
|
413
|
-
|
|
414
|
-
|
|
412
|
+
the dashboard are documented templates: the markup is the starting point, and
|
|
413
|
+
a page may add its own layouts and classes on top, with colours from the
|
|
414
|
+
tokens. The mockups, the sketches, the terminals and the tuis are guided kits:
|
|
415
|
+
they give an agent a frame, a floor and a set of rules, and the drawing is the
|
|
416
|
+
work.</p>
|
|
415
417
|
|
|
416
418
|
<div class="layouts">
|
|
417
419
|
<article class="layout">
|