rshow 0.0.5 → 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/reference/mockups.md +1 -0
- package/skills/rshow-slides/SKILL.md +2 -0
- package/themes/default/deck.css +325 -1
- package/themes/default/deck.js +169 -1
- 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
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 = {
|
|
@@ -279,6 +279,74 @@ body {
|
|
|
279
279
|
will-change: transform;
|
|
280
280
|
}
|
|
281
281
|
|
|
282
|
+
/* The key list and the small button that toggles it. Both sit in the free
|
|
283
|
+
bottom left corner of the stage, over the canvas and never in its way. */
|
|
284
|
+
|
|
285
|
+
.mk-keys-toggle {
|
|
286
|
+
position: absolute;
|
|
287
|
+
left: 12px;
|
|
288
|
+
bottom: 12px;
|
|
289
|
+
z-index: 4;
|
|
290
|
+
min-height: 28px;
|
|
291
|
+
padding: 0 8px;
|
|
292
|
+
font: inherit;
|
|
293
|
+
font-size: 12px;
|
|
294
|
+
line-height: 1;
|
|
295
|
+
color: var(--muted, #525252);
|
|
296
|
+
background: var(--bg, #ffffff);
|
|
297
|
+
border: 1px solid var(--border-subtle, #e0e0e0);
|
|
298
|
+
border-radius: 0;
|
|
299
|
+
cursor: pointer;
|
|
300
|
+
opacity: 0.75;
|
|
301
|
+
-webkit-appearance: none;
|
|
302
|
+
appearance: none;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
.mk-keys-toggle:hover,
|
|
306
|
+
.mk-keys-toggle[aria-expanded="true"] {
|
|
307
|
+
opacity: 1;
|
|
308
|
+
color: var(--fg, #161616);
|
|
309
|
+
border-color: var(--border-strong, #8d8d8d);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.mk-keys {
|
|
313
|
+
position: absolute;
|
|
314
|
+
left: 12px;
|
|
315
|
+
bottom: 48px;
|
|
316
|
+
z-index: 5;
|
|
317
|
+
width: 340px;
|
|
318
|
+
max-width: calc(100% - 24px);
|
|
319
|
+
padding: 12px 14px;
|
|
320
|
+
background: var(--bg, #ffffff);
|
|
321
|
+
border: 1px solid var(--border-subtle, #e0e0e0);
|
|
322
|
+
box-shadow: 0 2px 6px var(--border-subtle, #e0e0e0);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.mk-keys[hidden] {
|
|
326
|
+
display: none;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.mk-keys-list {
|
|
330
|
+
display: grid;
|
|
331
|
+
grid-template-columns: auto minmax(0, 1fr);
|
|
332
|
+
gap: 6px 16px;
|
|
333
|
+
margin: 0;
|
|
334
|
+
font-size: 13px;
|
|
335
|
+
line-height: 1.35;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.mk-keys-list dt {
|
|
339
|
+
font-family: var(--font-mono, ui-monospace, monospace);
|
|
340
|
+
font-size: 12px;
|
|
341
|
+
color: var(--fg, #161616);
|
|
342
|
+
white-space: nowrap;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.mk-keys-list dd {
|
|
346
|
+
margin: 0;
|
|
347
|
+
color: var(--muted, #525252);
|
|
348
|
+
}
|
|
349
|
+
|
|
282
350
|
.mk-row {
|
|
283
351
|
display: flex;
|
|
284
352
|
align-items: flex-start;
|
|
@@ -307,6 +375,18 @@ body {
|
|
|
307
375
|
--mk-h: 1112px;
|
|
308
376
|
}
|
|
309
377
|
|
|
378
|
+
/* The board the keys picked. It is a choice in the canvas and not a second
|
|
379
|
+
frame around the screen a reader is already looking at. */
|
|
380
|
+
.artboard.is-selected {
|
|
381
|
+
outline: 2px solid var(--accent, #0f62fe);
|
|
382
|
+
outline-offset: 6px;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.mockup[data-view="focus"] .artboard.is-selected,
|
|
386
|
+
.mockup[data-view="play"] .artboard.is-selected {
|
|
387
|
+
outline: none;
|
|
388
|
+
}
|
|
389
|
+
|
|
310
390
|
.mk-head {
|
|
311
391
|
display: flex;
|
|
312
392
|
align-items: center;
|
|
@@ -968,13 +1048,24 @@ body {
|
|
|
968
1048
|
}
|
|
969
1049
|
|
|
970
1050
|
@media (max-width: 520px) {
|
|
1051
|
+
/* A phone cannot hold the whole toolbar on one line: the groups wrap onto a
|
|
1052
|
+
second row, and a group that still does not fit shrinks to the row and
|
|
1053
|
+
scrolls inside itself, so no control ever sits past the right edge. */
|
|
971
1054
|
.mk-toolbar {
|
|
972
|
-
flex-wrap: nowrap;
|
|
973
1055
|
gap: 2px 6px;
|
|
974
1056
|
padding: 6px 10px;
|
|
975
1057
|
overflow-x: auto;
|
|
976
1058
|
}
|
|
977
1059
|
|
|
1060
|
+
.mk-toolbar > * {
|
|
1061
|
+
flex: 0 1 auto;
|
|
1062
|
+
min-width: 0;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
.mk-group {
|
|
1066
|
+
overflow-x: auto;
|
|
1067
|
+
}
|
|
1068
|
+
|
|
978
1069
|
.mk-field-text {
|
|
979
1070
|
display: none;
|
|
980
1071
|
}
|
|
@@ -982,6 +1073,10 @@ body {
|
|
|
982
1073
|
.mk-name {
|
|
983
1074
|
font-size: 13px;
|
|
984
1075
|
}
|
|
1076
|
+
|
|
1077
|
+
.mk-keys-toggle {
|
|
1078
|
+
min-height: 32px;
|
|
1079
|
+
}
|
|
985
1080
|
}
|
|
986
1081
|
|
|
987
1082
|
/* ---------------------------------------------------------------- views --- */
|
|
@@ -996,7 +1091,9 @@ body {
|
|
|
996
1091
|
display: none;
|
|
997
1092
|
}
|
|
998
1093
|
|
|
999
|
-
.mockup[data-view="play"] .mk-side
|
|
1094
|
+
.mockup[data-view="play"] .mk-side,
|
|
1095
|
+
.mockup[data-view="play"] .mk-keys,
|
|
1096
|
+
.mockup[data-view="play"] .mk-keys-toggle {
|
|
1000
1097
|
display: none;
|
|
1001
1098
|
}
|
|
1002
1099
|
}
|
|
@@ -1029,10 +1126,16 @@ body {
|
|
|
1029
1126
|
.mk-overlay,
|
|
1030
1127
|
.mk-statebar,
|
|
1031
1128
|
.mk-hint,
|
|
1129
|
+
.mk-keys,
|
|
1130
|
+
.mk-keys-toggle,
|
|
1032
1131
|
.rshow-mode-toggle {
|
|
1033
1132
|
display: none;
|
|
1034
1133
|
}
|
|
1035
1134
|
|
|
1135
|
+
.artboard.is-selected {
|
|
1136
|
+
outline: none;
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1036
1139
|
.mk-stage {
|
|
1037
1140
|
position: static;
|
|
1038
1141
|
overflow: visible;
|