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,703 @@
1
+ /*
2
+ * rshow theme "default": the tui kit, layer 4.
3
+ *
4
+ * A terminal user interface drawn as cells, for the mockups that are a tool
5
+ * rather than a window: a git client, a log tail, a deploy console. It is not a
6
+ * screenshot of a terminal and it is not a web page in a mono font: the drawing
7
+ * is a grid of character cells, every block is a whole number of cells wide and
8
+ * a whole number of lines tall, and the frame around a pane is drawn with the
9
+ * same box-drawing characters a real TUI would print.
10
+ *
11
+ * <div class="tui" data-cols="100" data-rows="27" data-palette="default"
12
+ * data-depth="256">
13
+ * <div class="tui-screen"> ... panes, status bar, key hints ... </div>
14
+ * </div>
15
+ *
16
+ * tui.js turns data-cols, data-rows, data-lines and data-title into the custom
17
+ * properties and the frames below, draws the gauges and sparklines, gates a
18
+ * terminal that is too small, and warns in the console when a pane does not fit
19
+ * its grid. Without tui.js the kit still renders: the properties have fallbacks,
20
+ * panes fall back to a 1px border, and no block is hidden.
21
+ *
22
+ * The rules this kit is drawn by, all of them enforced or checked by tui.js:
23
+ *
24
+ * 1. Cells, not pixels. Type is one size, the line height is fixed, and a
25
+ * block is sized with --tui-cols and --tui-lines. A half cell is a bug.
26
+ * 2. A terminal that is smaller than 80x24 is not a small terminal, it is a
27
+ * terminal asking to be resized: the grid is replaced by that message.
28
+ * 3. Exactly one pane has focus. Focus is a double frame and a bold title, so
29
+ * it survives a palette with one colour and a reader who cannot see colour.
30
+ * 4. Ctrl+C, Ctrl+Z and Ctrl+\ are the terminal's keys. A mockup that binds
31
+ * them teaches a reader something the app cannot keep.
32
+ * 5. Palettes degrade. Every palette is legible at data-depth="16", because a
33
+ * drawing that only works in true colour is a drawing that will be shown
34
+ * to someone with a 16 colour terminal, or a printer.
35
+ */
36
+
37
+ /* --------------------------------------------------------------- palette -- */
38
+
39
+ /* The default is a dark terminal with the six colours a TUI actually uses: a
40
+ dim grey for chrome, one accent, and green, red and yellow for state. */
41
+ .tui {
42
+ --tui-cols: 80;
43
+ --tui-rows: 24;
44
+ --tui-size: 13px;
45
+ --tui-line: 1.4;
46
+ --tui-cell-h: calc(var(--tui-line) * 1em);
47
+
48
+ --tui-bg: #121314;
49
+ --tui-fg: #d6d6d6;
50
+ --tui-dim: #7d848c;
51
+ --tui-line-color: #3a3f45;
52
+ --tui-fill: #1d2024;
53
+ --tui-accent: #6cb6ff;
54
+ --tui-add: #7ee787;
55
+ --tui-del: #ff8080;
56
+ --tui-warn: #e3b341;
57
+ --tui-sel-bg: #d6d6d6;
58
+ --tui-sel-fg: #121314;
59
+ --tui-status-bg: #21262b;
60
+ --tui-status-fg: #c9d1d9;
61
+ --tui-toast-bg: #21262b;
62
+ --tui-cursor: #d6d6d6;
63
+
64
+ box-sizing: border-box;
65
+ display: inline-block;
66
+ overflow: hidden;
67
+ font-family: var(--font-mono, ui-monospace, monospace);
68
+ font-size: var(--tui-size);
69
+ font-variant-ligatures: none;
70
+ line-height: var(--tui-line);
71
+ color: var(--tui-fg);
72
+ background: var(--tui-bg);
73
+ border-radius: 2px;
74
+ tab-size: 4;
75
+ }
76
+
77
+ .tui *,
78
+ .tui *::before,
79
+ .tui *::after {
80
+ box-sizing: border-box;
81
+ }
82
+
83
+ .tui[data-palette="dark"] {
84
+ --tui-bg: #1b1b1b;
85
+ --tui-fg: #d6d6d6;
86
+ --tui-dim: #8b8b8b;
87
+ --tui-line-color: #454545;
88
+ --tui-fill: #262626;
89
+ --tui-accent: #8ec07c;
90
+ --tui-add: #b8bb26;
91
+ --tui-del: #fb4934;
92
+ --tui-warn: #fabd2f;
93
+ --tui-sel-bg: #d6d6d6;
94
+ --tui-sel-fg: #1b1b1b;
95
+ --tui-status-bg: #262626;
96
+ --tui-status-fg: #d6d6d6;
97
+ --tui-toast-bg: #262626;
98
+ --tui-cursor: #d6d6d6;
99
+ }
100
+
101
+ .tui[data-palette="light"] {
102
+ --tui-bg: #fbfbfa;
103
+ --tui-fg: #232629;
104
+ --tui-dim: #6b7280;
105
+ --tui-line-color: #c9ccd1;
106
+ --tui-fill: #eceef1;
107
+ --tui-accent: #0b62a4;
108
+ --tui-add: #0f7b34;
109
+ --tui-del: #b3261e;
110
+ --tui-warn: #8a5a00;
111
+ --tui-sel-bg: #232629;
112
+ --tui-sel-fg: #fbfbfa;
113
+ --tui-status-bg: #eceef1;
114
+ --tui-status-fg: #232629;
115
+ --tui-toast-bg: #eceef1;
116
+ --tui-cursor: #232629;
117
+ }
118
+
119
+ /* A high contrast terminal is black on black and white on white: the frame is
120
+ white, the dim text is still readable, and the three states are the three
121
+ primaries. It is the palette to test a drawing in. */
122
+ .tui[data-palette="high-contrast"] {
123
+ --tui-bg: #000000;
124
+ --tui-fg: #ffffff;
125
+ --tui-dim: #c3c3c3;
126
+ --tui-line-color: #ffffff;
127
+ --tui-fill: #141414;
128
+ --tui-accent: #00ffff;
129
+ --tui-add: #00ff00;
130
+ --tui-del: #ff5555;
131
+ --tui-warn: #ffff00;
132
+ --tui-sel-bg: #ffffff;
133
+ --tui-sel-fg: #000000;
134
+ --tui-status-bg: #ffffff;
135
+ --tui-status-fg: #000000;
136
+ --tui-toast-bg: #000000;
137
+ --tui-cursor: #ffffff;
138
+ }
139
+
140
+ /* Mono is one colour and no colour at all: state is carried by a symbol, by
141
+ reverse video and by weight, which is what a reader with a monochrome
142
+ terminal, a printer or a colour deficiency sees. */
143
+ .tui[data-palette="mono"] {
144
+ --tui-bg: #0b0b0b;
145
+ --tui-fg: #e6e6e6;
146
+ --tui-dim: #949494;
147
+ --tui-line-color: #b4b4b4;
148
+ --tui-fill: #1c1c1c;
149
+ --tui-accent: #ffffff;
150
+ --tui-add: #e6e6e6;
151
+ --tui-del: #e6e6e6;
152
+ --tui-warn: #c9c9c9;
153
+ --tui-sel-bg: #e6e6e6;
154
+ --tui-sel-fg: #0b0b0b;
155
+ --tui-status-bg: #e6e6e6;
156
+ --tui-status-fg: #0b0b0b;
157
+ --tui-toast-bg: #1c1c1c;
158
+ --tui-cursor: #e6e6e6;
159
+ }
160
+
161
+ /* --------------------------------------------------------------- depth -- */
162
+
163
+ /* Sixteen colours is the floor: no true colour, no 256 colour cube, and nothing
164
+ subtle. The accent, the three states and the dim text are replaced with the
165
+ sixteen colour names so the same drawing is read the same way there. */
166
+ .tui[data-depth="16"] {
167
+ --tui-dim: #7f7f7f;
168
+ --tui-accent: #00cdcd;
169
+ --tui-add: #00cd00;
170
+ --tui-del: #cd0000;
171
+ --tui-warn: #cdcd00;
172
+ --tui-sel-bg: #e5e5e5;
173
+ --tui-sel-fg: #000000;
174
+ }
175
+
176
+ .tui[data-palette="light"][data-depth="16"] {
177
+ --tui-dim: #767676;
178
+ --tui-accent: #0000ee;
179
+ --tui-add: #007700;
180
+ --tui-del: #cd0000;
181
+ --tui-warn: #8a6d00;
182
+ --tui-sel-bg: #000000;
183
+ --tui-sel-fg: #ffffff;
184
+ }
185
+
186
+ .tui[data-palette="high-contrast"][data-depth="16"],
187
+ .tui[data-palette="mono"][data-depth="16"] {
188
+ --tui-accent: #ffffff;
189
+ --tui-add: #ffffff;
190
+ --tui-del: #ffffff;
191
+ --tui-warn: #d7d7d7;
192
+ }
193
+
194
+ /* --------------------------------------------------------------- screen -- */
195
+
196
+ .tui-screen {
197
+ position: relative;
198
+ width: calc(var(--tui-cols) * 1ch);
199
+ height: calc(var(--tui-rows) * var(--tui-cell-h));
200
+ overflow: hidden;
201
+ }
202
+
203
+ /* The minimum size gate. A terminal smaller than 80x24 is not a small terminal:
204
+ it is one that wants to be resized, and it says so instead of hiding the fact
205
+ that the drawing no longer fits. */
206
+ .tui-toosmall {
207
+ display: none;
208
+ width: calc(var(--tui-cols) * 1ch);
209
+ height: calc(var(--tui-rows) * var(--tui-cell-h));
210
+ padding: 1ch;
211
+ color: var(--tui-warn);
212
+ white-space: pre;
213
+ }
214
+
215
+ .tui[data-toosmall="true"] .tui-screen {
216
+ display: none;
217
+ }
218
+
219
+ .tui[data-toosmall="true"] .tui-toosmall {
220
+ display: block;
221
+ }
222
+
223
+ /* Layout helpers. A screen is a column of rows; a main region is a row of
224
+ panes. Both are sized in cells by their own --tui-cols and --tui-lines. */
225
+ .tui-body {
226
+ display: flex;
227
+ flex-direction: column;
228
+ height: 100%;
229
+ }
230
+
231
+ .tui-main {
232
+ display: flex;
233
+ flex: 1 1 auto;
234
+ gap: 0;
235
+ min-height: 0;
236
+ }
237
+
238
+ .tui-gap {
239
+ flex: 0 0 1ch;
240
+ }
241
+
242
+ .tui-grow {
243
+ flex: 1 1 auto;
244
+ min-width: 0;
245
+ }
246
+
247
+ /* ---------------------------------------------------------------- panes -- */
248
+
249
+ /* The frame itself is glyphs, drawn by tui.js into .tui-frame-* layers, because
250
+ a pane's title lives inside its top border and a border that is a line cannot
251
+ hold a title. Without tui.js the pane keeps a 1px border, which is the same
252
+ frame at the resolution of a browser instead of the resolution of a cell. */
253
+ .tui-pane {
254
+ position: relative;
255
+ display: flex;
256
+ flex-direction: column;
257
+ flex: 0 0 auto;
258
+ width: calc(var(--tui-cols, 40) * 1ch);
259
+ height: calc(var(--tui-lines, 10) * var(--tui-cell-h));
260
+ min-height: 0;
261
+ padding: var(--tui-cell-h) 1ch;
262
+ overflow: hidden;
263
+ border: 1px solid var(--tui-line-color);
264
+ }
265
+
266
+ /* Once tui.js has drawn the glyph frame, the 1px border has done its job and
267
+ goes away, so the box is exactly the cells it says it is and not two pixels
268
+ more. Without tui.js the border stays and the pane still reads as a box. */
269
+ .tui-pane.tui-framed,
270
+ .tui-dialog.tui-framed {
271
+ border-width: 0;
272
+ }
273
+
274
+ /* Focus is a double frame, a bold title and nothing that depends on colour: a
275
+ reader with one colour still sees which pane has the keyboard. */
276
+ /* Without the drawn frame the fallback marks focus with a 2px inner ring and a
277
+ bold title: still two signals, still not colour alone. */
278
+ .tui-pane[data-focus]:not(.tui-framed),
279
+ .tui-dialog[data-focus]:not(.tui-framed) {
280
+ border-color: var(--tui-fg);
281
+ box-shadow: inset 0 0 0 2px var(--tui-fg);
282
+ font-weight: 700;
283
+ }
284
+
285
+ .tui-frame {
286
+ position: absolute;
287
+ inset: 0;
288
+ overflow: hidden;
289
+ color: var(--tui-line-color);
290
+ white-space: pre;
291
+ pointer-events: none;
292
+ user-select: none;
293
+ }
294
+
295
+ .tui-frame-top,
296
+ .tui-frame-bottom {
297
+ position: absolute;
298
+ left: 0;
299
+ width: 100%;
300
+ height: var(--tui-cell-h);
301
+ line-height: var(--tui-cell-h);
302
+ white-space: pre;
303
+ }
304
+
305
+ .tui-frame-top {
306
+ top: 0;
307
+ }
308
+
309
+ .tui-frame-bottom {
310
+ bottom: 0;
311
+ }
312
+
313
+ .tui-frame-left,
314
+ .tui-frame-right {
315
+ position: absolute;
316
+ top: var(--tui-cell-h);
317
+ overflow: hidden;
318
+ line-height: var(--tui-cell-h);
319
+ white-space: pre;
320
+ }
321
+
322
+ .tui-frame-left {
323
+ left: 0;
324
+ }
325
+
326
+ .tui-frame-right {
327
+ right: 0;
328
+ }
329
+
330
+ /* The frame's own focus mark, set by tui.js when it draws a double frame, so a
331
+ pane demoted by a dialog loses the weight as well as the second line. */
332
+ .tui-pane.tui-frame-focus > .tui-frame,
333
+ .tui-dialog.tui-frame-focus > .tui-frame {
334
+ color: var(--tui-fg);
335
+ }
336
+
337
+ .tui-frame-focus .tui-frame-title {
338
+ font-weight: 700;
339
+ }
340
+
341
+ /* A pane's scroll body: every line is one cell tall, and overflow is a bug that
342
+ tui.js marks rather than a scrollbar a terminal would not draw. */
343
+ .tui-body-lines {
344
+ flex: 1 1 auto;
345
+ min-height: 0;
346
+ overflow: hidden;
347
+ }
348
+
349
+ /* ---------------------------------------------------------------- lists -- */
350
+
351
+ .tui-list {
352
+ display: flex;
353
+ flex-direction: column;
354
+ }
355
+
356
+ .tui-list-item {
357
+ display: block;
358
+ padding: 0;
359
+ overflow: hidden;
360
+ white-space: nowrap;
361
+ text-overflow: clip;
362
+ }
363
+
364
+ /* The classic selected row: reverse video, not a colour, not a bar. */
365
+ .tui-list-item[data-selected] {
366
+ color: var(--tui-sel-fg);
367
+ background: var(--tui-sel-bg);
368
+ }
369
+
370
+ .tui-list-item[data-selected] .tui-dim,
371
+ .tui-list-item[data-selected] .tui-accent {
372
+ color: inherit;
373
+ }
374
+
375
+ .tui-list-item[data-marker]::before {
376
+ content: attr(data-marker);
377
+ }
378
+
379
+ /* --------------------------------------------------------------- tables -- */
380
+
381
+ /* One cell of gutter on the right: a proportional column can land on a half
382
+ cell, and a glyph clipped by half a cell is the one thing a cell grid must not
383
+ do. */
384
+ .tui-table {
385
+ display: flex;
386
+ flex-direction: column;
387
+ width: calc(100% - 1ch);
388
+ --tui-table-cols: 12ch 1fr 8ch;
389
+ }
390
+
391
+ .tui-table-row {
392
+ display: grid;
393
+ grid-template-columns: var(--tui-table-cols);
394
+ gap: 1ch;
395
+ align-items: baseline;
396
+ white-space: nowrap;
397
+ }
398
+
399
+ .tui-table-row > * {
400
+ overflow: hidden;
401
+ text-overflow: clip;
402
+ }
403
+
404
+ .tui-table-row[data-head] {
405
+ color: var(--tui-dim);
406
+ border-bottom: 1px solid var(--tui-line-color);
407
+ }
408
+
409
+ .tui-table-row[data-align="right"] > * {
410
+ text-align: right;
411
+ }
412
+
413
+ .tui-table-cell[data-align="right"],
414
+ .tui-table-row > [data-align="right"] {
415
+ text-align: right;
416
+ }
417
+
418
+ /* ----------------------------------------------------- gauges and plots -- */
419
+
420
+ /* A gauge is block characters, so it is a whole number of cells wide and it
421
+ reads in a 16 colour terminal and on a printer. */
422
+ .tui-gauge {
423
+ white-space: pre;
424
+ letter-spacing: 0;
425
+ color: var(--tui-accent);
426
+ }
427
+
428
+ .tui-gauge[data-tone="warn"] {
429
+ color: var(--tui-warn);
430
+ }
431
+
432
+ .tui-gauge[data-tone="del"] {
433
+ color: var(--tui-del);
434
+ }
435
+
436
+ .tui-gauge .tui-gauge-empty {
437
+ color: var(--tui-dim);
438
+ }
439
+
440
+ .tui-sparkline {
441
+ white-space: pre;
442
+ letter-spacing: 0;
443
+ color: var(--tui-accent);
444
+ }
445
+
446
+ /* ------------------------------------------------------------------ tabs -- */
447
+
448
+ .tui-tabs {
449
+ display: flex;
450
+ gap: 1ch;
451
+ white-space: nowrap;
452
+ }
453
+
454
+ .tui-tab {
455
+ padding: 0 1ch;
456
+ color: var(--tui-dim);
457
+ }
458
+
459
+ .tui-tab[data-active],
460
+ .tui-tab.is-active {
461
+ color: var(--tui-sel-fg);
462
+ background: var(--tui-sel-bg);
463
+ font-weight: 700;
464
+ }
465
+
466
+ /* ----------------------------------------------------------------- input -- */
467
+
468
+ .tui-input {
469
+ display: inline-flex;
470
+ align-items: center;
471
+ min-height: var(--tui-cell-h);
472
+ max-width: 100%;
473
+ padding: 0;
474
+ overflow: hidden;
475
+ white-space: pre;
476
+ color: var(--tui-fg);
477
+ background: var(--tui-fill);
478
+ }
479
+
480
+ .tui-input[data-label]::before {
481
+ content: attr(data-label) " ";
482
+ color: var(--tui-dim);
483
+ }
484
+
485
+ .tui-cursor {
486
+ display: inline-block;
487
+ width: 1ch;
488
+ height: 1em;
489
+ background: var(--tui-cursor);
490
+ vertical-align: text-bottom;
491
+ animation: tui-blink 1.1s steps(1, end) infinite;
492
+ }
493
+
494
+ @keyframes tui-blink {
495
+ 0%,
496
+ 50% {
497
+ opacity: 1;
498
+ }
499
+
500
+ 50.01%,
501
+ 100% {
502
+ opacity: 0.15;
503
+ }
504
+ }
505
+
506
+ @media (prefers-reduced-motion: reduce) {
507
+ .tui-cursor {
508
+ animation: none;
509
+ }
510
+ }
511
+
512
+ /* ------------------------------------------------------------ status bar -- */
513
+
514
+ .tui-statusbar {
515
+ display: flex;
516
+ align-items: center;
517
+ gap: 1ch;
518
+ min-height: var(--tui-cell-h);
519
+ padding: 0 1ch;
520
+ color: var(--tui-status-fg);
521
+ background: var(--tui-status-bg);
522
+ white-space: nowrap;
523
+ overflow: hidden;
524
+ }
525
+
526
+ .tui-statusbar .tui-dim {
527
+ color: inherit;
528
+ opacity: 0.75;
529
+ }
530
+
531
+ /* ------------------------------------------------------------- key hints -- */
532
+
533
+ .tui-keys {
534
+ display: flex;
535
+ align-items: center;
536
+ gap: 1ch;
537
+ min-height: var(--tui-cell-h);
538
+ padding: 0 1ch;
539
+ color: var(--tui-dim);
540
+ white-space: nowrap;
541
+ overflow: hidden;
542
+ }
543
+
544
+ .tui-keys kbd {
545
+ font: inherit;
546
+ color: var(--tui-fg);
547
+ background: var(--tui-fill);
548
+ border: 0;
549
+ }
550
+
551
+ .tui-keys kbd::before {
552
+ content: "[";
553
+ color: var(--tui-dim);
554
+ }
555
+
556
+ .tui-keys kbd::after {
557
+ content: "]";
558
+ color: var(--tui-dim);
559
+ }
560
+
561
+ .tui-keys .tui-gap {
562
+ flex: 0 0 2ch;
563
+ }
564
+
565
+ /* -------------------------------------------------------------- dialogs -- */
566
+
567
+ /* A dialog is a pane that sits over the grid: same frame, double frame, and the
568
+ columns behind it stay visible so a reader sees what the dialog is about. */
569
+ .tui-dialog {
570
+ position: absolute;
571
+ top: 50%;
572
+ left: 50%;
573
+ z-index: 2;
574
+ display: flex;
575
+ flex-direction: column;
576
+ width: calc(var(--tui-cols, 46) * 1ch);
577
+ height: calc(var(--tui-lines, 8) * var(--tui-cell-h));
578
+ padding: var(--tui-cell-h) 1ch;
579
+ overflow: hidden;
580
+ color: var(--tui-fg);
581
+ background: var(--tui-bg);
582
+ border: 1px solid var(--tui-fg);
583
+ transform: translate(-50%, -50%);
584
+ }
585
+
586
+ .tui-dialog > .tui-frame {
587
+ color: var(--tui-fg);
588
+ }
589
+
590
+ .tui-dialog-body {
591
+ flex: 1 1 auto;
592
+ min-height: 0;
593
+ overflow: hidden;
594
+ }
595
+
596
+ .tui-dialog-actions {
597
+ display: flex;
598
+ gap: 2ch;
599
+ white-space: nowrap;
600
+ }
601
+
602
+ /* A button in a terminal is a word in reverse video, and the default one is
603
+ marked with a symbol as well, so the drawing survives mono. */
604
+ .tui-button {
605
+ padding: 0 1ch;
606
+ color: var(--tui-sel-fg);
607
+ background: var(--tui-sel-bg);
608
+ }
609
+
610
+ .tui-button[data-variant="quiet"] {
611
+ color: var(--tui-fg);
612
+ background: transparent;
613
+ padding: 0;
614
+ }
615
+
616
+ /* --------------------------------------------------------------- toasts -- */
617
+
618
+ .tui-toast {
619
+ position: absolute;
620
+ bottom: calc(2 * var(--tui-cell-h));
621
+ left: 50%;
622
+ z-index: 3;
623
+ display: flex;
624
+ align-items: center;
625
+ gap: 1ch;
626
+ padding: 0 1ch;
627
+ color: var(--tui-toast-fg, var(--tui-fg));
628
+ background: var(--tui-toast-bg);
629
+ border: 1px solid var(--tui-line-color);
630
+ transform: translateX(-50%);
631
+ white-space: nowrap;
632
+ }
633
+
634
+ .tui-toast[data-tone="ok"] {
635
+ border-color: var(--tui-add);
636
+ }
637
+
638
+ .tui-toast[data-tone="warn"] {
639
+ border-color: var(--tui-warn);
640
+ }
641
+
642
+ .tui-toast[data-tone="error"] {
643
+ border-color: var(--tui-del);
644
+ }
645
+
646
+ /* -------------------------------------------------------------- semantic -- */
647
+
648
+ .tui-dim {
649
+ color: var(--tui-dim);
650
+ }
651
+
652
+ .tui-accent {
653
+ color: var(--tui-accent);
654
+ }
655
+
656
+ .tui-add {
657
+ color: var(--tui-add);
658
+ }
659
+
660
+ .tui-del {
661
+ color: var(--tui-del);
662
+ }
663
+
664
+ .tui-warn {
665
+ color: var(--tui-warn);
666
+ }
667
+
668
+ .tui-bold {
669
+ font-weight: 700;
670
+ }
671
+
672
+ .tui-reverse {
673
+ color: var(--tui-sel-fg);
674
+ background: var(--tui-sel-bg);
675
+ }
676
+
677
+ .tui-rule {
678
+ height: var(--tui-cell-h);
679
+ overflow: hidden;
680
+ color: var(--tui-line-color);
681
+ white-space: pre;
682
+ }
683
+
684
+ .tui-line {
685
+ height: var(--tui-cell-h);
686
+ overflow: hidden;
687
+ white-space: pre;
688
+ }
689
+
690
+ /* ------------------------------------------------------------ the check -- */
691
+
692
+ /* tui.js adds this to an element whose content no longer fits its cells. It is
693
+ loud on purpose: a mockup that silently clips a line teaches the reader the
694
+ wrong thing about the screen. */
695
+ .tui-overflow {
696
+ outline: 2px dashed var(--tui-del);
697
+ outline-offset: -2px;
698
+ }
699
+
700
+ /* Inside a mockup the terminal is the screen: no page margin, no page font. */
701
+ .mk-screen .tui {
702
+ margin: 0;
703
+ }