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
|
@@ -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;
|
package/themes/default/mockup.js
CHANGED
|
@@ -55,6 +55,13 @@
|
|
|
55
55
|
* link holds a screen. Escape goes back to the canvas. ?skin= and ?view= and
|
|
56
56
|
* ?screen= set the first frame, which is what a screenshot wants.
|
|
57
57
|
*
|
|
58
|
+
* Keys
|
|
59
|
+
*
|
|
60
|
+
* w a s d and the arrows pan the canvas, + - 0 and z zoom it, [ ] and Tab
|
|
61
|
+
* pick a screen, Enter opens it and Escape goes back to the canvas. 1 to 9
|
|
62
|
+
* pick a state of the screen the keys are on, and ? lists every one of them.
|
|
63
|
+
* A field keeps its own keys and a chord with meta, ctrl or alt is never ours.
|
|
64
|
+
*
|
|
58
65
|
* Comments are read and written at data-store, default ../_data/mockup-comments
|
|
59
66
|
* relative to the page, and the store holds {value: [{id, artboard, text, at}]}.
|
|
60
67
|
* The server records who wrote it: this file never sends an author.
|
|
@@ -464,7 +471,7 @@
|
|
|
464
471
|
modeHost.appendChild(modeButton);
|
|
465
472
|
toolbar.appendChild(modeHost);
|
|
466
473
|
|
|
467
|
-
var hints = el('span', 'mk-hint', 'Drag to pan, Ctrl and wheel to zoom,
|
|
474
|
+
var hints = el('span', 'mk-hint', 'Drag to pan, Ctrl and wheel to zoom, ? for the keys');
|
|
468
475
|
toolbar.appendChild(hints);
|
|
469
476
|
|
|
470
477
|
// The bar that names the screen in focus and in play.
|
|
@@ -529,6 +536,54 @@
|
|
|
529
536
|
main.appendChild(stage);
|
|
530
537
|
main.appendChild(side);
|
|
531
538
|
|
|
539
|
+
// ---------------------------------------------------------------- keys ---
|
|
540
|
+
|
|
541
|
+
var KEYS = [
|
|
542
|
+
['w a s d', 'Pan the canvas'],
|
|
543
|
+
['arrow keys', 'Pan the canvas, or step between screens'],
|
|
544
|
+
['Shift and a key', 'Pan a quarter of the stage'],
|
|
545
|
+
['+ -', 'Zoom in and out'],
|
|
546
|
+
['0', 'Fit every screen'],
|
|
547
|
+
['z', 'Zoom to 100%'],
|
|
548
|
+
['[ ] or Tab', 'Previous and next screen'],
|
|
549
|
+
['Enter', 'Open the selected screen'],
|
|
550
|
+
['1 to 9', 'Pick a state of that screen'],
|
|
551
|
+
['Esc', 'Back to the canvas, or close this list'],
|
|
552
|
+
['?', 'Show or hide this list']
|
|
553
|
+
];
|
|
554
|
+
|
|
555
|
+
var keysOpen = false;
|
|
556
|
+
|
|
557
|
+
var keysPanel = el('div', 'mk-keys');
|
|
558
|
+
keysPanel.setAttribute('role', 'dialog');
|
|
559
|
+
keysPanel.setAttribute('aria-label', 'Keys');
|
|
560
|
+
keysPanel.hidden = true;
|
|
561
|
+
var keysList = el('dl', 'mk-keys-list');
|
|
562
|
+
KEYS.forEach(function (row) {
|
|
563
|
+
keysList.appendChild(el('dt', null, row[0]));
|
|
564
|
+
keysList.appendChild(el('dd', null, row[1]));
|
|
565
|
+
});
|
|
566
|
+
keysPanel.appendChild(keysList);
|
|
567
|
+
stage.appendChild(keysPanel);
|
|
568
|
+
|
|
569
|
+
// The corner button says what it is, so a reader who never guesses ? still
|
|
570
|
+
// finds the list. The pointerdown guard leaves the buttons of the stage alone.
|
|
571
|
+
var keysButton = el('button', 'mk-keys-toggle', '? keys');
|
|
572
|
+
keysButton.type = 'button';
|
|
573
|
+
keysButton.setAttribute('aria-label', 'Keys');
|
|
574
|
+
keysButton.setAttribute('aria-expanded', 'false');
|
|
575
|
+
keysButton.setAttribute('title', 'Every key (?)');
|
|
576
|
+
keysButton.addEventListener('click', function () {
|
|
577
|
+
setKeys(!keysOpen);
|
|
578
|
+
});
|
|
579
|
+
stage.appendChild(keysButton);
|
|
580
|
+
|
|
581
|
+
function setKeys(open) {
|
|
582
|
+
keysOpen = open === true;
|
|
583
|
+
keysPanel.hidden = !keysOpen;
|
|
584
|
+
keysButton.setAttribute('aria-expanded', keysOpen ? 'true' : 'false');
|
|
585
|
+
}
|
|
586
|
+
|
|
532
587
|
// Rows: wide screens and phones pack side by side, everything stays in the
|
|
533
588
|
// same visual order as the markup.
|
|
534
589
|
function layoutRows() {
|
|
@@ -605,6 +660,7 @@
|
|
|
605
660
|
}
|
|
606
661
|
|
|
607
662
|
function fitAll() {
|
|
663
|
+
stopAnimation();
|
|
608
664
|
var box = stageBox();
|
|
609
665
|
var w = world.offsetWidth;
|
|
610
666
|
var h = world.offsetHeight;
|
|
@@ -616,24 +672,33 @@
|
|
|
616
672
|
applyTransform(true);
|
|
617
673
|
}
|
|
618
674
|
|
|
619
|
-
function
|
|
675
|
+
function clampPoint(x, y) {
|
|
620
676
|
var box = stageBox();
|
|
621
677
|
var w = world.offsetWidth * zoom;
|
|
622
678
|
var h = world.offsetHeight * zoom;
|
|
623
679
|
var pad = 80;
|
|
680
|
+
var at = { x: x, y: y };
|
|
624
681
|
if (w <= box.width) {
|
|
625
|
-
|
|
682
|
+
at.x = (box.width - w) / 2;
|
|
626
683
|
} else {
|
|
627
|
-
|
|
684
|
+
at.x = Math.min(pad, Math.max(box.width - w - pad, at.x));
|
|
628
685
|
}
|
|
629
686
|
if (h <= box.height) {
|
|
630
|
-
|
|
687
|
+
at.y = (box.height - h) / 2;
|
|
631
688
|
} else {
|
|
632
|
-
|
|
689
|
+
at.y = Math.min(pad, Math.max(box.height - h - pad, at.y));
|
|
633
690
|
}
|
|
691
|
+
return at;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
function clamp() {
|
|
695
|
+
var at = clampPoint(tx, ty);
|
|
696
|
+
tx = at.x;
|
|
697
|
+
ty = at.y;
|
|
634
698
|
}
|
|
635
699
|
|
|
636
700
|
function zoomTo(next, clientX, clientY) {
|
|
701
|
+
stopAnimation();
|
|
637
702
|
var box = stageBox();
|
|
638
703
|
var sx = clientX === undefined ? box.width / 2 : clientX - box.left;
|
|
639
704
|
var sy = clientY === undefined ? box.height / 2 : clientY - box.top;
|
|
@@ -670,6 +735,144 @@
|
|
|
670
735
|
zoomTo(0.1);
|
|
671
736
|
}
|
|
672
737
|
|
|
738
|
+
// ------------------------------------------------------------ selection --
|
|
739
|
+
|
|
740
|
+
var selectedId = null;
|
|
741
|
+
var panAnimation = null;
|
|
742
|
+
|
|
743
|
+
function markSelected() {
|
|
744
|
+
for (var b = 0; b < boards.length; b += 1) {
|
|
745
|
+
boards[b].el.classList.toggle('is-selected', boards[b].id === selectedId);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
function selectedBoard() {
|
|
750
|
+
return view === 'canvas' ? boardById(selectedId) : current();
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
function pickState(index) {
|
|
754
|
+
var board = selectedBoard();
|
|
755
|
+
if (!board) {
|
|
756
|
+
return false;
|
|
757
|
+
}
|
|
758
|
+
var names = [''].concat(board.states);
|
|
759
|
+
if (index < 0 || index >= names.length) {
|
|
760
|
+
return false;
|
|
761
|
+
}
|
|
762
|
+
setState(board, names[index]);
|
|
763
|
+
return true;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
// Where a board sits in the stage, measured with the canvas transform out of
|
|
767
|
+
// the way, so a selection lands on it at whatever the zoom is.
|
|
768
|
+
function boardAt(board) {
|
|
769
|
+
var box = stageBox();
|
|
770
|
+
var saved = world.style.transform;
|
|
771
|
+
world.style.transform = 'translate(0px,0px) scale(1)';
|
|
772
|
+
var rect = board.el.getBoundingClientRect();
|
|
773
|
+
world.style.transform = saved;
|
|
774
|
+
return { x: rect.left - box.left, y: rect.top - box.top, w: rect.width, h: rect.height };
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
function stopAnimation() {
|
|
778
|
+
if (panAnimation !== null) {
|
|
779
|
+
if (typeof window.cancelAnimationFrame === 'function') {
|
|
780
|
+
window.cancelAnimationFrame(panAnimation.frame);
|
|
781
|
+
}
|
|
782
|
+
window.clearTimeout(panAnimation.timer);
|
|
783
|
+
panAnimation = null;
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
function animateTo(x, y) {
|
|
788
|
+
stopAnimation();
|
|
789
|
+
var run = { frame: null, timer: null };
|
|
790
|
+
panAnimation = run;
|
|
791
|
+
|
|
792
|
+
function finish() {
|
|
793
|
+
if (panAnimation !== run) {
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
panAnimation = null;
|
|
797
|
+
tx = x;
|
|
798
|
+
ty = y;
|
|
799
|
+
applyTransform(true);
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
if (typeof window.requestAnimationFrame !== 'function') {
|
|
803
|
+
finish();
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
var fromX = tx;
|
|
807
|
+
var fromY = ty;
|
|
808
|
+
var started = null;
|
|
809
|
+
|
|
810
|
+
function frame(now) {
|
|
811
|
+
if (panAnimation !== run) {
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
var at = typeof now === 'number' ? now : 0;
|
|
815
|
+
if (started === null) {
|
|
816
|
+
started = at;
|
|
817
|
+
}
|
|
818
|
+
var t = Math.min(1, (at - started) / 220);
|
|
819
|
+
var eased = 1 - (1 - t) * (1 - t) * (1 - t);
|
|
820
|
+
tx = fromX + (x - fromX) * eased;
|
|
821
|
+
ty = fromY + (y - fromY) * eased;
|
|
822
|
+
applyTransform(true);
|
|
823
|
+
if (t < 1) {
|
|
824
|
+
run.frame = window.requestAnimationFrame(frame);
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
finish();
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
run.frame = window.requestAnimationFrame(frame);
|
|
831
|
+
// A window that draws no frames, such as the headless one behind a
|
|
832
|
+
// screenshot, still lands on the screen the keys picked.
|
|
833
|
+
run.timer = window.setTimeout(finish, 320);
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
function centreOn(board, animate) {
|
|
837
|
+
var at = boardAt(board);
|
|
838
|
+
var box = stageBox();
|
|
839
|
+
var to = clampPoint(
|
|
840
|
+
(box.width - at.w * zoom) / 2 - at.x * zoom,
|
|
841
|
+
(box.height - at.h * zoom) / 2 - at.y * zoom
|
|
842
|
+
);
|
|
843
|
+
if (animate !== true) {
|
|
844
|
+
tx = to.x;
|
|
845
|
+
ty = to.y;
|
|
846
|
+
applyTransform(true);
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
animateTo(to.x, to.y);
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
function selectBoard(index) {
|
|
853
|
+
var count = boards.length;
|
|
854
|
+
if (count === 0) {
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
var at = ((index % count) + count) % count;
|
|
858
|
+
selectedId = boards[at].id;
|
|
859
|
+
markSelected();
|
|
860
|
+
if (view === 'canvas') {
|
|
861
|
+
centreOn(boards[at], true);
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
function stepSelection(delta) {
|
|
866
|
+
if (boards.length === 0) {
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
var at = selectedId === null ? -1 : boardIndex(selectedId);
|
|
870
|
+
if (at < 0) {
|
|
871
|
+
at = delta < 0 ? boards.length : -1;
|
|
872
|
+
}
|
|
873
|
+
selectBoard(at + delta);
|
|
874
|
+
}
|
|
875
|
+
|
|
673
876
|
function markCurrent() {
|
|
674
877
|
for (var b = 0; b < boards.length; b += 1) {
|
|
675
878
|
boards[b].el.classList.toggle('is-current', boards[b].id === currentId);
|
|
@@ -677,6 +880,7 @@
|
|
|
677
880
|
}
|
|
678
881
|
|
|
679
882
|
function layoutCurrent() {
|
|
883
|
+
stopAnimation();
|
|
680
884
|
var board = current();
|
|
681
885
|
if (!board) {
|
|
682
886
|
return;
|
|
@@ -744,6 +948,8 @@
|
|
|
744
948
|
canvasState = { zoom: zoom, tx: tx, ty: ty };
|
|
745
949
|
}
|
|
746
950
|
currentId = id;
|
|
951
|
+
selectedId = id;
|
|
952
|
+
markSelected();
|
|
747
953
|
setView('focus');
|
|
748
954
|
writeHash(id);
|
|
749
955
|
}
|
|
@@ -756,13 +962,21 @@
|
|
|
756
962
|
canvasState = { zoom: zoom, tx: tx, ty: ty };
|
|
757
963
|
}
|
|
758
964
|
currentId = id;
|
|
965
|
+
selectedId = id;
|
|
966
|
+
markSelected();
|
|
759
967
|
setView('play');
|
|
760
968
|
writeHash(id);
|
|
761
969
|
}
|
|
762
970
|
|
|
763
971
|
function backToCanvas() {
|
|
972
|
+
if (view !== 'canvas' && currentId !== null) {
|
|
973
|
+
// The screen the reader was on stays the selected one, so the keys can
|
|
974
|
+
// open it again or step from it.
|
|
975
|
+
selectedId = currentId;
|
|
976
|
+
}
|
|
764
977
|
currentId = null;
|
|
765
978
|
setView('canvas');
|
|
979
|
+
markSelected();
|
|
766
980
|
writeHash('');
|
|
767
981
|
zoom = canvasState.zoom;
|
|
768
982
|
tx = canvasState.tx;
|
|
@@ -1105,6 +1319,17 @@
|
|
|
1105
1319
|
var flashTimer = null;
|
|
1106
1320
|
var suppressHash = false;
|
|
1107
1321
|
|
|
1322
|
+
// A control placed over the canvas owns its own pointer and its own keys.
|
|
1323
|
+
var CONTROLS =
|
|
1324
|
+
'button, a[href], input, select, textarea, label, summary, [role="button"], [tabindex]:not([tabindex="-1"])';
|
|
1325
|
+
|
|
1326
|
+
function control(node) {
|
|
1327
|
+
if (!node || !node.closest) {
|
|
1328
|
+
return null;
|
|
1329
|
+
}
|
|
1330
|
+
return node.closest(CONTROLS);
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1108
1333
|
toolbar.addEventListener('click', function (event) {
|
|
1109
1334
|
var button = event.target.closest ? event.target.closest('[data-mk]') : null;
|
|
1110
1335
|
if (!button) {
|
|
@@ -1198,6 +1423,7 @@
|
|
|
1198
1423
|
});
|
|
1199
1424
|
|
|
1200
1425
|
function startPan(event) {
|
|
1426
|
+
stopAnimation();
|
|
1201
1427
|
dragging = { x: event.clientX, y: event.clientY, tx: tx, ty: ty };
|
|
1202
1428
|
stage.classList.add('is-panning');
|
|
1203
1429
|
if (stage.setPointerCapture) {
|
|
@@ -1213,6 +1439,12 @@
|
|
|
1213
1439
|
if (event.button !== 0) {
|
|
1214
1440
|
return;
|
|
1215
1441
|
}
|
|
1442
|
+
// A control above the screen keeps its own click: capturing the pointer
|
|
1443
|
+
// here would retarget that click to the stage.
|
|
1444
|
+
var taken = control(event.target);
|
|
1445
|
+
if (!spaceDown && taken !== null && taken !== stage) {
|
|
1446
|
+
return;
|
|
1447
|
+
}
|
|
1216
1448
|
var inScreen = event.target.closest ? event.target.closest('.mk-screen') : null;
|
|
1217
1449
|
if (!spaceDown && inScreen) {
|
|
1218
1450
|
return;
|
|
@@ -1254,6 +1486,7 @@
|
|
|
1254
1486
|
return;
|
|
1255
1487
|
}
|
|
1256
1488
|
event.preventDefault();
|
|
1489
|
+
stopAnimation();
|
|
1257
1490
|
if (event.ctrlKey || event.metaKey) {
|
|
1258
1491
|
zoomTo(zoom * (event.deltaY < 0 ? 1.12 : 1 / 1.12), event.clientX, event.clientY);
|
|
1259
1492
|
return;
|
|
@@ -1274,6 +1507,32 @@
|
|
|
1274
1507
|
return tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT' || node.isContentEditable === true;
|
|
1275
1508
|
}
|
|
1276
1509
|
|
|
1510
|
+
// Tab and Enter belong to the page while a reader is in the toolbar, the
|
|
1511
|
+
// panel or a screen: only the stage itself takes them over.
|
|
1512
|
+
function fromStage(node) {
|
|
1513
|
+
return node === document.body || node === document.documentElement || node === stage;
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
function panSize(event) {
|
|
1517
|
+
if (event.shiftKey === true) {
|
|
1518
|
+
var box = stageBox();
|
|
1519
|
+
return { x: Math.round(box.width / 4), y: Math.round(box.height / 4) };
|
|
1520
|
+
}
|
|
1521
|
+
return { x: 64, y: 64 };
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
// A screen taller than the stage scrolls under it. One that fits has nowhere
|
|
1525
|
+
// to go, so up and down leave it alone.
|
|
1526
|
+
function panTall(direction, event) {
|
|
1527
|
+
var board = current();
|
|
1528
|
+
var box = stageBox();
|
|
1529
|
+
if (!board || board.el.offsetHeight * zoom <= box.height) {
|
|
1530
|
+
return;
|
|
1531
|
+
}
|
|
1532
|
+
ty -= direction * panSize(event).y;
|
|
1533
|
+
applyTransform(false);
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1277
1536
|
document.addEventListener('keydown', function (event) {
|
|
1278
1537
|
if (event.metaKey || event.ctrlKey || event.altKey || typing(event.target)) {
|
|
1279
1538
|
return;
|
|
@@ -1286,6 +1545,29 @@
|
|
|
1286
1545
|
}
|
|
1287
1546
|
return;
|
|
1288
1547
|
}
|
|
1548
|
+
if (key === '?') {
|
|
1549
|
+
event.preventDefault();
|
|
1550
|
+
setKeys(!keysOpen);
|
|
1551
|
+
return;
|
|
1552
|
+
}
|
|
1553
|
+
if (key === 'Escape') {
|
|
1554
|
+
if (keysOpen) {
|
|
1555
|
+
event.preventDefault();
|
|
1556
|
+
setKeys(false);
|
|
1557
|
+
return;
|
|
1558
|
+
}
|
|
1559
|
+
if (view !== 'canvas') {
|
|
1560
|
+
event.preventDefault();
|
|
1561
|
+
backToCanvas();
|
|
1562
|
+
}
|
|
1563
|
+
return;
|
|
1564
|
+
}
|
|
1565
|
+
if (key >= '1' && key <= '9' && key.length === 1) {
|
|
1566
|
+
if (pickState(Number(key) - 1)) {
|
|
1567
|
+
event.preventDefault();
|
|
1568
|
+
}
|
|
1569
|
+
return;
|
|
1570
|
+
}
|
|
1289
1571
|
if (key === '+' || key === '=') {
|
|
1290
1572
|
event.preventDefault();
|
|
1291
1573
|
zoomStep(1);
|
|
@@ -1303,33 +1585,67 @@
|
|
|
1303
1585
|
}
|
|
1304
1586
|
return;
|
|
1305
1587
|
}
|
|
1306
|
-
if (key === '
|
|
1307
|
-
if (view
|
|
1588
|
+
if (key === 'z' || key === 'Z') {
|
|
1589
|
+
if (view === 'canvas') {
|
|
1308
1590
|
event.preventDefault();
|
|
1309
|
-
|
|
1591
|
+
zoomTo(1);
|
|
1310
1592
|
}
|
|
1311
1593
|
return;
|
|
1312
1594
|
}
|
|
1313
|
-
if (key === '
|
|
1314
|
-
|
|
1595
|
+
if (key === ']' || key === '[') {
|
|
1596
|
+
event.preventDefault();
|
|
1597
|
+
var order = key === ']' ? 1 : -1;
|
|
1315
1598
|
if (view === 'canvas') {
|
|
1316
|
-
|
|
1317
|
-
clamp();
|
|
1318
|
-
applyTransform(true);
|
|
1599
|
+
stepSelection(order);
|
|
1319
1600
|
} else {
|
|
1601
|
+
step(order);
|
|
1602
|
+
}
|
|
1603
|
+
return;
|
|
1604
|
+
}
|
|
1605
|
+
if (key === 'Tab') {
|
|
1606
|
+
if (view === 'canvas' && fromStage(event.target) && control(event.target) === null) {
|
|
1320
1607
|
event.preventDefault();
|
|
1321
|
-
|
|
1608
|
+
stepSelection(event.shiftKey === true ? -1 : 1);
|
|
1322
1609
|
}
|
|
1323
1610
|
return;
|
|
1324
1611
|
}
|
|
1325
|
-
if (key === '
|
|
1326
|
-
if (view !== 'canvas') {
|
|
1612
|
+
if (key === 'Enter') {
|
|
1613
|
+
if (view !== 'canvas' || selectedId === null || control(event.target) !== null) {
|
|
1327
1614
|
return;
|
|
1328
1615
|
}
|
|
1329
|
-
|
|
1616
|
+
event.preventDefault();
|
|
1617
|
+
focus(selectedId);
|
|
1618
|
+
return;
|
|
1619
|
+
}
|
|
1620
|
+
var dx = 0;
|
|
1621
|
+
var dy = 0;
|
|
1622
|
+
if (key === 'ArrowRight' || key === 'd' || key === 'D') {
|
|
1623
|
+
dx = 1;
|
|
1624
|
+
} else if (key === 'ArrowLeft' || key === 'a' || key === 'A') {
|
|
1625
|
+
dx = -1;
|
|
1626
|
+
} else if (key === 'ArrowDown' || key === 's' || key === 'S') {
|
|
1627
|
+
dy = 1;
|
|
1628
|
+
} else if (key === 'ArrowUp' || key === 'w' || key === 'W') {
|
|
1629
|
+
dy = -1;
|
|
1630
|
+
}
|
|
1631
|
+
if (dx === 0 && dy === 0) {
|
|
1632
|
+
return;
|
|
1633
|
+
}
|
|
1634
|
+
event.preventDefault();
|
|
1635
|
+
if (view === 'canvas') {
|
|
1636
|
+
var stepSize = panSize(event);
|
|
1637
|
+
stopAnimation();
|
|
1638
|
+
tx -= dx * stepSize.x;
|
|
1639
|
+
ty -= dy * stepSize.y;
|
|
1330
1640
|
clamp();
|
|
1331
1641
|
applyTransform(true);
|
|
1642
|
+
return;
|
|
1643
|
+
}
|
|
1644
|
+
if (dx !== 0) {
|
|
1645
|
+
step(dx);
|
|
1646
|
+
return;
|
|
1332
1647
|
}
|
|
1648
|
+
panTall(dy, event);
|
|
1333
1649
|
});
|
|
1334
1650
|
|
|
1335
1651
|
document.addEventListener('keyup', function (event) {
|
|
@@ -1412,6 +1728,12 @@
|
|
|
1412
1728
|
|
|
1413
1729
|
var requested = query.get('screen') || window.location.hash.replace(/^#/, '');
|
|
1414
1730
|
var startView = query.get('view');
|
|
1731
|
+
// A phone shows the whole canvas at 12%, which nobody can read. When the stage
|
|
1732
|
+
// is narrower than 700px and the URL asks for no view and no screen, the first
|
|
1733
|
+
// screen opens in focus view, fitted to the width by layoutCurrent. ?view=play,
|
|
1734
|
+
// ?view=canvas and #screen still say what they said.
|
|
1735
|
+
var narrow = stage.getBoundingClientRect().width < 700;
|
|
1736
|
+
var asked = startView !== null && startView !== '';
|
|
1415
1737
|
main.setAttribute('data-view', 'canvas');
|
|
1416
1738
|
fitAll();
|
|
1417
1739
|
if (boards.length > 0) {
|
|
@@ -1425,6 +1747,8 @@
|
|
|
1425
1747
|
play(boards[0].id);
|
|
1426
1748
|
} else if (startView === 'focus') {
|
|
1427
1749
|
focus(boards[0].id);
|
|
1750
|
+
} else if (narrow && !asked) {
|
|
1751
|
+
focus(boards[0].id);
|
|
1428
1752
|
}
|
|
1429
1753
|
}
|
|
1430
1754
|
|
|
@@ -241,6 +241,14 @@
|
|
|
241
241
|
tab-size: 8;
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
+
/* A recording the width cannot hold keeps the grid it was taken on and scrolls
|
|
245
|
+
here, rather than being fitted down to a font nobody can read. The player
|
|
246
|
+
clips its own box, so the scroll has to be said again one class deeper. */
|
|
247
|
+
.terminal .terminal-body .ap-player {
|
|
248
|
+
overflow-x: auto;
|
|
249
|
+
overscroll-behavior-x: contain;
|
|
250
|
+
}
|
|
251
|
+
|
|
244
252
|
/* asciinema-player declares a dark palette of its own on .ap-player, which
|
|
245
253
|
would win over anything inherited from the figure. Hand it the figure's
|
|
246
254
|
palette instead, so a recording and a capture of the same program agree. */
|