rshow 0.0.0-stage → 0.0.2

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 (84) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +50 -2
  3. package/dist/main.js +8500 -0
  4. package/package.json +48 -4
  5. package/skills/rshow/SKILL.md +144 -0
  6. package/skills/rshow/reference/cli.md +263 -0
  7. package/skills/rshow/reference/dashboard.md +122 -0
  8. package/skills/rshow/reference/mockups.md +113 -0
  9. package/skills/rshow/reference/page-contract.md +83 -0
  10. package/skills/rshow/reference/pages.md +174 -0
  11. package/skills/rshow/reference/poster.md +137 -0
  12. package/skills/rshow/reference/project-page.md +132 -0
  13. package/skills/rshow/reference/reports.md +158 -0
  14. package/skills/rshow/reference/sketch.md +78 -0
  15. package/skills/rshow/reference/terminal.md +148 -0
  16. package/skills/rshow/reference/themes.md +139 -0
  17. package/skills/rshow-implement/SKILL.md +112 -0
  18. package/skills/rshow-implement/reference/compare.md +156 -0
  19. package/skills/rshow-implement/reference/handoff.md +125 -0
  20. package/skills/rshow-implement/reference/map.md +151 -0
  21. package/skills/rshow-slides/SKILL.md +136 -0
  22. package/skills/rshow-slides/reference/layouts.md +251 -0
  23. package/themes/default/dashboard.css +521 -0
  24. package/themes/default/dashboard.js +1390 -0
  25. package/themes/default/deck.css +586 -0
  26. package/themes/default/deck.js +741 -0
  27. package/themes/default/example/capture.ans +1 -0
  28. package/themes/default/example/dashboard.html +106 -0
  29. package/themes/default/example/demo.cast +14 -0
  30. package/themes/default/example/drawing.excalidraw +377 -0
  31. package/themes/default/example/index.html +311 -0
  32. package/themes/default/example/mockup-desktop.html +331 -0
  33. package/themes/default/example/mockup-platforms.html +742 -0
  34. package/themes/default/example/mockup.html +416 -0
  35. package/themes/default/example/page.html +153 -0
  36. package/themes/default/example/poster-figure.svg +58 -0
  37. package/themes/default/example/poster.html +181 -0
  38. package/themes/default/example/project.html +229 -0
  39. package/themes/default/example/report.html +404 -0
  40. package/themes/default/example/results.csv +61 -0
  41. package/themes/default/example/sketch.html +178 -0
  42. package/themes/default/example/teaser.svg +66 -0
  43. package/themes/default/example/terminal.html +231 -0
  44. package/themes/default/example/tui.html +223 -0
  45. package/themes/default/fonts/IBMPlexMono-Regular-Latin1.woff2 +0 -0
  46. package/themes/default/fonts/IBMPlexSans-Light-Latin1.woff2 +0 -0
  47. package/themes/default/fonts/IBMPlexSans-Regular-Latin1.woff2 +0 -0
  48. package/themes/default/fonts/IBMPlexSans-SemiBold-Latin1.woff2 +0 -0
  49. package/themes/default/fonts/OFL.txt +93 -0
  50. package/themes/default/fonts.css +63 -0
  51. package/themes/default/index.html +573 -0
  52. package/themes/default/mockup.css +1078 -0
  53. package/themes/default/mockup.js +1433 -0
  54. package/themes/default/mode.js +242 -0
  55. package/themes/default/page.css +320 -0
  56. package/themes/default/poster.css +480 -0
  57. package/themes/default/poster.js +223 -0
  58. package/themes/default/project.css +482 -0
  59. package/themes/default/project.js +116 -0
  60. package/themes/default/report.css +776 -0
  61. package/themes/default/report.js +427 -0
  62. package/themes/default/sketch.css +166 -0
  63. package/themes/default/sketch.js +681 -0
  64. package/themes/default/skins/adwaita.css +669 -0
  65. package/themes/default/skins/custom.css +596 -0
  66. package/themes/default/skins/egui.css +639 -0
  67. package/themes/default/skins/fluent.css +1093 -0
  68. package/themes/default/skins/fluent.js +458 -0
  69. package/themes/default/skins/mac.css +647 -0
  70. package/themes/default/skins/material.css +692 -0
  71. package/themes/default/skins/sketch.css +436 -0
  72. package/themes/default/skins/sketch.js +494 -0
  73. package/themes/default/skins/wire.css +561 -0
  74. package/themes/default/terminal.css +292 -0
  75. package/themes/default/terminal.js +433 -0
  76. package/themes/default/theme.json +149 -0
  77. package/themes/default/tokens.css +255 -0
  78. package/themes/default/tui.css +703 -0
  79. package/themes/default/tui.js +472 -0
  80. package/worker/index.js +6918 -0
  81. package/worker/migrations/0001_init.sql +52 -0
  82. package/worker/migrations/0002_blobs.sql +5 -0
  83. package/worker/migrations/0003_projects.sql +31 -0
  84. package/worker/migrations/0004_page_data_blob.sql +19 -0
@@ -0,0 +1,242 @@
1
+ /*
2
+ * rshow theme "default": mode. Zero dependencies, no build step.
3
+ *
4
+ * One theme, two modes, system by default. This file owns the reader's choice:
5
+ *
6
+ * localStorage["rshow-mode"] = "light" | "dark" | "system" (absence = system)
7
+ * <html data-mode="light"> force light
8
+ * <html data-mode="dark"> force dark
9
+ * <html> follow prefers-color-scheme
10
+ *
11
+ * It runs from <head>, synchronously, before the body paints, so a page never
12
+ * flashes white on the way to dark. Everything that touches storage sits in a
13
+ * try/catch: private windows and file:// refuse localStorage and the page still
14
+ * works, following the system.
15
+ *
16
+ * The toggle. A page may place its own <button data-rshow-mode-toggle>; if it
17
+ * does not, this file mounts a small fixed button in the bottom right corner of
18
+ * the viewport, 16px in from the edge and outside whatever a notch or a home
19
+ * indicator takes (env(safe-area-inset-*)), so it holds still on a long page.
20
+ * A report page has a note column down its right side, and at 1056px and wider
21
+ * the corner button would sit on top of it: there the button moves to the
22
+ * bottom of the contents column instead, which sticks and is never covered.
23
+ * Either way the button is a real <button> that says the current mode in words,
24
+ * has an aria-label, a 44px target, the Carbon ghost look and no place in
25
+ * print.
26
+ *
27
+ * API: window.rshowMode = { get, set, cycle }. A deck reads the same key, so a
28
+ * choice made on one rshow document holds on the next: see deck.js, key "t".
29
+ * A change also fires a "rshowmodechange" event on document, which is how the
30
+ * theme preview re-reads its token values.
31
+ */
32
+ (function () {
33
+ 'use strict';
34
+
35
+ var KEY = 'rshow-mode';
36
+ var MODES = ['system', 'light', 'dark'];
37
+ var WORDS = { system: 'System', light: 'Light', dark: 'Dark' };
38
+ var LABELS = {
39
+ system: 'Colour mode: System, following this device. Activate to choose light.',
40
+ light: 'Colour mode: Light. Activate to choose dark.',
41
+ dark: 'Colour mode: Dark. Activate to follow this device.'
42
+ };
43
+
44
+ function normalize(value) {
45
+ return MODES.indexOf(value) === -1 ? 'system' : value;
46
+ }
47
+
48
+ function get() {
49
+ try {
50
+ return normalize(window.localStorage.getItem(KEY));
51
+ } catch (err) {
52
+ return 'system';
53
+ }
54
+ }
55
+
56
+ function paint(mode) {
57
+ var root = document.documentElement;
58
+ if (mode === 'system') {
59
+ root.removeAttribute('data-mode');
60
+ } else {
61
+ root.setAttribute('data-mode', mode);
62
+ }
63
+ }
64
+
65
+ function announce(mode) {
66
+ try {
67
+ document.dispatchEvent(new window.CustomEvent('rshowmodechange', {
68
+ detail: { mode: mode }
69
+ }));
70
+ } catch (err) {
71
+ // A browser without CustomEvent keeps the attribute and loses the event.
72
+ }
73
+ }
74
+
75
+ function set(mode) {
76
+ var next = normalize(mode);
77
+ paint(next);
78
+ try {
79
+ if (next === 'system') {
80
+ window.localStorage.removeItem(KEY);
81
+ } else {
82
+ window.localStorage.setItem(KEY, next);
83
+ }
84
+ } catch (err) {
85
+ // No storage: the mode still holds for this document.
86
+ }
87
+ sync();
88
+ announce(next);
89
+ return next;
90
+ }
91
+
92
+ function cycle() {
93
+ return set(MODES[(MODES.indexOf(get()) + 1) % MODES.length]);
94
+ }
95
+
96
+ // ---------------------------------------------------------------- paint --
97
+
98
+ // Before anything else, so the first frame is the right ground.
99
+ paint(get());
100
+
101
+ var STYLE = [
102
+ '.rshow-mode-toggle{',
103
+ 'min-width:44px;min-height:44px;padding:0 14px;',
104
+ 'display:inline-flex;align-items:center;justify-content:center;',
105
+ 'font-family:var(--font-sans,system-ui,sans-serif);font-size:14px;line-height:1;',
106
+ 'letter-spacing:0;color:var(--muted,#525252);background:transparent;',
107
+ 'border:1px solid var(--border-subtle,#e0e0e0);border-radius:var(--radius,0);',
108
+ 'cursor:pointer;-webkit-appearance:none;appearance:none;',
109
+ 'transition:background-color 110ms ease,color 110ms ease,border-color 110ms ease}',
110
+ '.rshow-mode-toggle:hover{background:var(--layer-01,#f4f4f4);',
111
+ 'border-color:var(--border-strong,#8d8d8d);color:var(--fg,#161616)}',
112
+ '.rshow-mode-toggle:focus-visible{outline:2px solid var(--accent,#0f62fe);',
113
+ 'outline-offset:-2px}',
114
+ '.rshow-mode-toggle--fixed{position:fixed;z-index:20;',
115
+ 'right:16px;bottom:16px;',
116
+ 'right:max(16px,env(safe-area-inset-right,16px));',
117
+ 'bottom:max(16px,env(safe-area-inset-bottom,16px));',
118
+ 'background:var(--bg,#ffffff)}',
119
+ '.rshow-mode-toggle--toc{margin:var(--space-06,24px) 0 0}',
120
+ '@media print{.rshow-mode-toggle,.rshow-mode-toggle--fixed,',
121
+ '.rshow-mode-toggle--toc{display:none}}'
122
+ ].join('');
123
+
124
+ var button = null;
125
+
126
+ function sync() {
127
+ if (!button) {
128
+ return;
129
+ }
130
+ var mode = get();
131
+ button.textContent = WORDS[mode];
132
+ button.setAttribute('aria-label', LABELS[mode]);
133
+ button.setAttribute('title', 'Colour mode: ' + WORDS[mode] + ' (t in a deck)');
134
+ }
135
+
136
+ function mount() {
137
+ var style = document.createElement('style');
138
+ style.appendChild(document.createTextNode(STYLE));
139
+ document.head.appendChild(style);
140
+
141
+ var host = document.querySelector('[data-rshow-mode-toggle]');
142
+ var fixed = false;
143
+ if (host) {
144
+ if (host.tagName === 'BUTTON') {
145
+ button = host;
146
+ } else {
147
+ button = document.createElement('button');
148
+ button.type = 'button';
149
+ host.appendChild(button);
150
+ }
151
+ } else {
152
+ button = document.createElement('button');
153
+ button.type = 'button';
154
+ fixed = true;
155
+ document.body.appendChild(button);
156
+ }
157
+
158
+ button.type = 'button';
159
+ button.classList.add('rshow-mode-toggle');
160
+ if (fixed) {
161
+ button.classList.add('rshow-mode-toggle--fixed');
162
+ }
163
+ button.addEventListener('click', function () {
164
+ cycle();
165
+ });
166
+ sync();
167
+ if (fixed) {
168
+ place();
169
+ window.addEventListener('resize', queuePlace);
170
+ document.addEventListener('rshowmodechange', place);
171
+ }
172
+ }
173
+
174
+ // ---------------------------------------------------------- placement --
175
+
176
+ // This file mounts the button, so it owns where it goes. The corner rule is
177
+ // position: fixed, 16px from the right and bottom edge of the viewport, and
178
+ // the only thing it can land on is the note column of a report. The test is
179
+ // geometry, not a media query alone: if the corner box and the note column
180
+ // share any width at a size where a note column exists, the button goes to
181
+ // the bottom of the contents instead. It is measured from the corner, never
182
+ // from where the button happens to be, so the two places cannot flip.
183
+
184
+ var CORNER = 16;
185
+ var queued = null;
186
+
187
+ function cornerOverNotes() {
188
+ var notes = document.querySelector('.report .notes');
189
+ if (!notes) {
190
+ return false;
191
+ }
192
+ if (window.matchMedia && !window.matchMedia('(min-width: 1056px)').matches) {
193
+ return false;
194
+ }
195
+ var column = notes.getBoundingClientRect();
196
+ if (column.width === 0 || column.height === 0) {
197
+ return false;
198
+ }
199
+ var width = button.getBoundingClientRect().width;
200
+ var right = window.innerWidth - CORNER;
201
+ var left = right - width - 4;
202
+ return left < column.right && column.left < right;
203
+ }
204
+
205
+ function place() {
206
+ if (!button) {
207
+ return;
208
+ }
209
+ var toc = document.querySelector('.report nav.toc');
210
+ if (toc && cornerOverNotes()) {
211
+ if (button.parentNode !== toc) {
212
+ toc.appendChild(button);
213
+ }
214
+ button.classList.add('rshow-mode-toggle--toc');
215
+ button.classList.remove('rshow-mode-toggle--fixed');
216
+ return;
217
+ }
218
+ if (button.parentNode !== document.body) {
219
+ document.body.appendChild(button);
220
+ }
221
+ button.classList.add('rshow-mode-toggle--fixed');
222
+ button.classList.remove('rshow-mode-toggle--toc');
223
+ }
224
+
225
+ function queuePlace() {
226
+ if (queued !== null) {
227
+ window.clearTimeout(queued);
228
+ }
229
+ queued = window.setTimeout(function () {
230
+ queued = null;
231
+ place();
232
+ }, 120);
233
+ }
234
+
235
+ if (document.body) {
236
+ mount();
237
+ } else {
238
+ document.addEventListener('DOMContentLoaded', mount);
239
+ }
240
+
241
+ window.rshowMode = { get: get, set: set, cycle: cycle };
242
+ }());
@@ -0,0 +1,320 @@
1
+ /*
2
+ * rshow theme "default": page layout
3
+ *
4
+ * For artifacts that are not a deck: reports, notes, explainers, reference
5
+ * pages. Link order: fonts.css, tokens.css, page.css.
6
+ *
7
+ * The page is Carbon white, the same system as the deck. It follows the system
8
+ * appearance into Carbon g100 and lets a reader override that choice; the
9
+ * colours come from tokens.css, which holds both modes. Prose stops at about 72
10
+ * characters, and a table, a figure and a code block are set the same way here
11
+ * as on a slide.
12
+ *
13
+ * No eyebrow label, no card grid, no numbered tiles, no accent rule down the
14
+ * side of a quote. A heading says what the section holds and the type does the
15
+ * rest.
16
+ */
17
+
18
+ *,
19
+ *::before,
20
+ *::after {
21
+ box-sizing: border-box;
22
+ }
23
+
24
+ html {
25
+ color-scheme: var(--color-scheme);
26
+ -webkit-text-size-adjust: 100%;
27
+ }
28
+
29
+ body {
30
+ margin: 0;
31
+ padding: var(--space-11) var(--space-06) var(--space-13);
32
+ background: var(--bg);
33
+ color: var(--fg);
34
+ font-family: var(--font-sans);
35
+ font-size: var(--text-sm);
36
+ line-height: 1.6;
37
+ -webkit-font-smoothing: antialiased;
38
+ text-rendering: optimizeLegibility;
39
+ }
40
+
41
+ main {
42
+ /* A ch is the width of a zero, which is wider than an average letter, so
43
+ 56ch of Plex Sans at 18px is about 72 characters: the measure a reader can
44
+ hold on one line. */
45
+ max-width: 56ch;
46
+ margin: 0 auto;
47
+ }
48
+
49
+ :focus-visible {
50
+ outline: 2px solid var(--accent);
51
+ outline-offset: 2px;
52
+ }
53
+
54
+ /* ------------------------------------------------------------------ prose */
55
+
56
+ h1,
57
+ h2,
58
+ h3,
59
+ h4 {
60
+ font-weight: 400;
61
+ line-height: var(--lh-heading);
62
+ letter-spacing: 0;
63
+ text-wrap: balance;
64
+ }
65
+
66
+ h1 {
67
+ margin: 0 0 var(--space-05);
68
+ font-size: var(--text-2xl);
69
+ letter-spacing: -0.01em;
70
+ }
71
+
72
+ h2 {
73
+ margin: var(--space-09) 0 var(--space-04);
74
+ font-size: var(--text-lg);
75
+ font-weight: 600;
76
+ }
77
+
78
+ h3 {
79
+ margin: var(--space-07) 0 var(--space-03);
80
+ font-size: var(--text-base);
81
+ font-weight: 600;
82
+ }
83
+
84
+ /* A fourth level is still a sentence in the body font, not a label above a
85
+ heading. There is no eyebrow in this system. */
86
+ h4 {
87
+ margin: var(--space-06) 0 var(--space-03);
88
+ font-size: var(--text-sm);
89
+ font-weight: 600;
90
+ color: var(--muted);
91
+ }
92
+
93
+ p {
94
+ margin: 0 0 var(--space-05);
95
+ }
96
+
97
+ .lede {
98
+ margin: 0 0 var(--space-07);
99
+ font-size: var(--text-base);
100
+ line-height: 1.5;
101
+ color: var(--muted);
102
+ max-width: 52ch;
103
+ }
104
+
105
+ ul,
106
+ ol {
107
+ margin: 0 0 var(--space-05);
108
+ padding-left: var(--space-06);
109
+ }
110
+
111
+ li {
112
+ margin: 0 0 var(--space-02);
113
+ }
114
+
115
+ li::marker {
116
+ color: var(--muted);
117
+ }
118
+
119
+ a {
120
+ color: var(--accent);
121
+ text-decoration: underline;
122
+ text-decoration-thickness: 1px;
123
+ text-underline-offset: 2px;
124
+ }
125
+
126
+ strong {
127
+ font-weight: 600;
128
+ }
129
+
130
+ hr {
131
+ height: 0;
132
+ margin: var(--space-08) 0;
133
+ border: 0;
134
+ border-top: 1px solid var(--border-subtle);
135
+ }
136
+
137
+ blockquote {
138
+ margin: var(--space-06) 0;
139
+ font-size: var(--text-base);
140
+ line-height: 1.5;
141
+ max-width: 44ch;
142
+ }
143
+
144
+ blockquote p:last-child {
145
+ margin-bottom: 0;
146
+ }
147
+
148
+ blockquote cite {
149
+ display: block;
150
+ margin-top: var(--space-03);
151
+ font-style: normal;
152
+ font-size: var(--text-xs);
153
+ color: var(--muted);
154
+ }
155
+
156
+ .small {
157
+ font-size: var(--text-2xs);
158
+ line-height: 1.5;
159
+ color: var(--muted);
160
+ }
161
+
162
+ .muted {
163
+ color: var(--muted);
164
+ }
165
+
166
+ /* Mono is for code and for data, never for a label. */
167
+ .mono,
168
+ code,
169
+ kbd,
170
+ pre,
171
+ samp {
172
+ font-family: var(--font-mono);
173
+ font-size: 0.92em;
174
+ }
175
+
176
+ /* ------------------------------------------------------------------ code */
177
+
178
+ code {
179
+ padding: 0.1em 0.32em;
180
+ background: var(--layer-01);
181
+ border-radius: var(--radius-sm);
182
+ }
183
+
184
+ pre {
185
+ margin: 0 0 var(--space-06);
186
+ padding: var(--space-05) var(--space-06);
187
+ overflow: auto;
188
+ background: var(--layer-01);
189
+ border: 0;
190
+ border-radius: var(--radius);
191
+ line-height: var(--lh-code);
192
+ }
193
+
194
+ pre code {
195
+ padding: 0;
196
+ background: none;
197
+ font-size: inherit;
198
+ }
199
+
200
+ kbd {
201
+ padding: 0.1em 0.36em;
202
+ border: 1px solid var(--border-strong);
203
+ background: var(--layer-01);
204
+ color: var(--fg);
205
+ }
206
+
207
+ /* ---------------------------------------------------------------- tables */
208
+
209
+ /* Overflow belongs to the wrapper so a phone scrolls the table, not the page. */
210
+ .table {
211
+ margin: 0 0 var(--space-07);
212
+ overflow-x: auto;
213
+ }
214
+
215
+ table {
216
+ width: 100%;
217
+ border-collapse: collapse;
218
+ font-size: var(--text-xs);
219
+ line-height: 1.45;
220
+ }
221
+
222
+ caption {
223
+ padding: 0 0 var(--space-04);
224
+ text-align: left;
225
+ font-size: var(--text-2xs);
226
+ color: var(--muted);
227
+ }
228
+
229
+ th,
230
+ td {
231
+ padding: var(--space-03) var(--space-05);
232
+ text-align: left;
233
+ vertical-align: baseline;
234
+ border-bottom: 1px solid var(--border-subtle);
235
+ }
236
+
237
+ thead th {
238
+ font-size: var(--text-2xs);
239
+ font-weight: 600;
240
+ color: var(--muted);
241
+ border-bottom: 1px solid var(--border-strong);
242
+ }
243
+
244
+ tbody tr:last-child th,
245
+ tbody tr:last-child td {
246
+ border-bottom: 0;
247
+ }
248
+
249
+ td.num-cell,
250
+ th.num-cell {
251
+ text-align: right;
252
+ font-variant-numeric: tabular-nums;
253
+ }
254
+
255
+ /* --------------------------------------------------------------- figures */
256
+
257
+ figure {
258
+ margin: var(--space-07) 0;
259
+ }
260
+
261
+ figure img,
262
+ figure svg {
263
+ display: block;
264
+ max-width: 100%;
265
+ height: auto;
266
+ }
267
+
268
+ figcaption {
269
+ margin-top: var(--space-04);
270
+ font-size: var(--text-2xs);
271
+ line-height: 1.5;
272
+ color: var(--muted);
273
+ }
274
+
275
+ /* ---------------------------------------------------------------- footer */
276
+
277
+ footer {
278
+ margin-top: var(--space-11);
279
+ padding-top: var(--space-05);
280
+ border-top: 1px solid var(--border-subtle);
281
+ font-size: var(--text-2xs);
282
+ line-height: 1.6;
283
+ color: var(--muted);
284
+ }
285
+
286
+ footer a {
287
+ color: var(--muted);
288
+ }
289
+
290
+ /* --------------------------------------------------------- small screens */
291
+
292
+ @media (max-width: 640px) {
293
+ body {
294
+ padding: var(--space-08) var(--space-05) var(--space-11);
295
+ font-size: var(--text-xs);
296
+ }
297
+
298
+ h1 {
299
+ font-size: var(--text-xl);
300
+ }
301
+
302
+ h2 {
303
+ font-size: var(--text-md);
304
+ }
305
+
306
+ blockquote {
307
+ font-size: var(--text-sm);
308
+ }
309
+
310
+ pre {
311
+ font-size: var(--text-2xs);
312
+ }
313
+ }
314
+
315
+ @media (prefers-reduced-motion: reduce) {
316
+ * {
317
+ animation-duration: 0.01ms !important;
318
+ transition-duration: 0.01ms !important;
319
+ }
320
+ }