foss-design 0.9.12 → 0.9.14
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/cli.js +1064 -176
- package/dist/viewer/assets/index-Dg5nDlgF.css +2 -0
- package/dist/viewer/assets/index-xErZjcMe.js +163 -0
- package/dist/viewer/index.html +2 -2
- package/dist/viewer-lib/index.js +4106 -1858
- package/dist/viewer-lib/styles.css +115 -19
- package/dist/viewer-lib/types/src/shared/drawings.d.ts +153 -0
- package/dist/viewer-lib/types/viewer/src/lib/drawings.d.ts +61 -0
- package/dist/viewer-lib/types/viewer/src/lib/ripple.d.ts +7 -0
- package/dist/viewer-lib/types/viewer/src/lib/source.d.ts +6 -0
- package/dist/viewer-lib/types/viewer/src/library.d.ts +1 -0
- package/dist/viewer-lib/types/viewer/src/pages/canvas/board.d.ts +16 -0
- package/dist/viewer-lib/types/viewer/src/pages/canvas/camera.d.ts +7 -0
- package/dist/viewer-lib/types/viewer/src/pages/canvas/canvas-bar.d.ts +12 -1
- package/dist/viewer-lib/types/viewer/src/pages/canvas/drawing/editor.d.ts +68 -0
- package/dist/viewer-lib/types/viewer/src/pages/canvas/drawing/strokes.d.ts +15 -0
- package/dist/viewer-lib/types/viewer/src/pages/canvas/drawing-capture.d.ts +18 -0
- package/dist/viewer-lib/types/viewer/src/pages/canvas/markup.d.ts +26 -0
- package/dist/viewer-lib/types/viewer/src/pages/canvas/view-state.d.ts +2 -0
- package/dist/viewer-lib/types/viewer/src/pages/canvas/viewport.d.ts +4 -2
- package/dist/viewer-lib/types/viewer/src/ui/icon-paths.d.ts +7 -0
- package/package.json +4 -1
- package/dist/viewer/assets/index-BUDcnDgZ.css +0 -2
- package/dist/viewer/assets/index-Cm4CEv8V.js +0 -159
|
@@ -458,18 +458,22 @@
|
|
|
458
458
|
position: sticky;
|
|
459
459
|
}
|
|
460
460
|
|
|
461
|
-
.-inset-px {
|
|
462
|
-
inset: -1px;
|
|
463
|
-
}
|
|
464
|
-
|
|
465
461
|
.inset-0 {
|
|
466
462
|
inset: 0;
|
|
467
463
|
}
|
|
468
464
|
|
|
465
|
+
.inset-4 {
|
|
466
|
+
inset: calc(var(--spacing) * 4);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
469
|
.inset-x-0 {
|
|
470
470
|
inset-inline: 0;
|
|
471
471
|
}
|
|
472
472
|
|
|
473
|
+
.inset-x-4 {
|
|
474
|
+
inset-inline: calc(var(--spacing) * 4);
|
|
475
|
+
}
|
|
476
|
+
|
|
473
477
|
.inset-x-5 {
|
|
474
478
|
inset-inline: calc(var(--spacing) * 5);
|
|
475
479
|
}
|
|
@@ -534,6 +538,10 @@
|
|
|
534
538
|
bottom: calc(var(--spacing) * 5);
|
|
535
539
|
}
|
|
536
540
|
|
|
541
|
+
.bottom-\[76px\] {
|
|
542
|
+
bottom: 76px;
|
|
543
|
+
}
|
|
544
|
+
|
|
537
545
|
.bottom-full {
|
|
538
546
|
bottom: 100%;
|
|
539
547
|
}
|
|
@@ -739,11 +747,21 @@
|
|
|
739
747
|
height: 7px;
|
|
740
748
|
}
|
|
741
749
|
|
|
750
|
+
.size-\[12px\] {
|
|
751
|
+
width: 12px;
|
|
752
|
+
height: 12px;
|
|
753
|
+
}
|
|
754
|
+
|
|
742
755
|
.size-\[14px\] {
|
|
743
756
|
width: 14px;
|
|
744
757
|
height: 14px;
|
|
745
758
|
}
|
|
746
759
|
|
|
760
|
+
.size-\[92px\] {
|
|
761
|
+
width: 92px;
|
|
762
|
+
height: 92px;
|
|
763
|
+
}
|
|
764
|
+
|
|
747
765
|
.size-full {
|
|
748
766
|
width: 100%;
|
|
749
767
|
height: 100%;
|
|
@@ -923,6 +941,10 @@
|
|
|
923
941
|
min-height: calc(var(--spacing) * 7);
|
|
924
942
|
}
|
|
925
943
|
|
|
944
|
+
.min-h-8 {
|
|
945
|
+
min-height: calc(var(--spacing) * 8);
|
|
946
|
+
}
|
|
947
|
+
|
|
926
948
|
.min-h-9 {
|
|
927
949
|
min-height: calc(var(--spacing) * 9);
|
|
928
950
|
}
|
|
@@ -977,6 +999,10 @@
|
|
|
977
999
|
width: calc(var(--spacing) * 14);
|
|
978
1000
|
}
|
|
979
1001
|
|
|
1002
|
+
.w-\[26px\] {
|
|
1003
|
+
width: 26px;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
980
1006
|
.w-\[32px\] {
|
|
981
1007
|
width: 32px;
|
|
982
1008
|
}
|
|
@@ -1053,10 +1079,6 @@
|
|
|
1053
1079
|
max-width: 80%;
|
|
1054
1080
|
}
|
|
1055
1081
|
|
|
1056
|
-
.max-w-\[95\%\] {
|
|
1057
|
-
max-width: 95%;
|
|
1058
|
-
}
|
|
1059
|
-
|
|
1060
1082
|
.max-w-\[260px\] {
|
|
1061
1083
|
max-width: 260px;
|
|
1062
1084
|
}
|
|
@@ -1129,6 +1151,10 @@
|
|
|
1129
1151
|
flex: 1;
|
|
1130
1152
|
}
|
|
1131
1153
|
|
|
1154
|
+
.flex-\[1_1_auto\] {
|
|
1155
|
+
flex: auto;
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1132
1158
|
.shrink {
|
|
1133
1159
|
flex-shrink: 1;
|
|
1134
1160
|
}
|
|
@@ -1149,6 +1175,10 @@
|
|
|
1149
1175
|
flex-basis: 0;
|
|
1150
1176
|
}
|
|
1151
1177
|
|
|
1178
|
+
.origin-top-left {
|
|
1179
|
+
transform-origin: 0 0;
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1152
1182
|
.-translate-x-1\/2 {
|
|
1153
1183
|
--tw-translate-x: calc(calc(1 / 2 * 100%) * -1);
|
|
1154
1184
|
translate: var(--tw-translate-x) var(--tw-translate-y);
|
|
@@ -1211,6 +1241,10 @@
|
|
|
1211
1241
|
resize: both;
|
|
1212
1242
|
}
|
|
1213
1243
|
|
|
1244
|
+
.resize-none {
|
|
1245
|
+
resize: none;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1214
1248
|
.resize-y {
|
|
1215
1249
|
resize: vertical;
|
|
1216
1250
|
}
|
|
@@ -1223,6 +1257,10 @@
|
|
|
1223
1257
|
grid-template-columns: 92px minmax(0, 1fr);
|
|
1224
1258
|
}
|
|
1225
1259
|
|
|
1260
|
+
.grid-cols-\[minmax\(var\(--dock-left\,0px\)\,1fr\)_minmax\(0\,auto\)_minmax\(var\(--dock-right\,0px\)\,1fr\)\] {
|
|
1261
|
+
grid-template-columns: minmax(var(--dock-left, 0px),1fr) minmax(0,auto) minmax(var(--dock-right, 0px),1fr);
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1226
1264
|
.grid-rows-\[20px_auto_20px\] {
|
|
1227
1265
|
grid-template-rows: 20px auto 20px;
|
|
1228
1266
|
}
|
|
@@ -1331,6 +1369,10 @@
|
|
|
1331
1369
|
row-gap: calc(var(--spacing) * .5);
|
|
1332
1370
|
}
|
|
1333
1371
|
|
|
1372
|
+
.gap-y-1 {
|
|
1373
|
+
row-gap: var(--spacing);
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1334
1376
|
.self-center {
|
|
1335
1377
|
align-self: center;
|
|
1336
1378
|
}
|
|
@@ -1353,6 +1395,10 @@
|
|
|
1353
1395
|
overflow: hidden;
|
|
1354
1396
|
}
|
|
1355
1397
|
|
|
1398
|
+
.overflow-visible {
|
|
1399
|
+
overflow: visible;
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1356
1402
|
.overflow-x-auto {
|
|
1357
1403
|
overflow-x: auto;
|
|
1358
1404
|
}
|
|
@@ -1429,11 +1475,6 @@
|
|
|
1429
1475
|
border-width: 0;
|
|
1430
1476
|
}
|
|
1431
1477
|
|
|
1432
|
-
.border-2 {
|
|
1433
|
-
border-style: var(--tw-border-style);
|
|
1434
|
-
border-width: 2px;
|
|
1435
|
-
}
|
|
1436
|
-
|
|
1437
1478
|
.border-t {
|
|
1438
1479
|
border-top-style: var(--tw-border-style);
|
|
1439
1480
|
border-top-width: 1px;
|
|
@@ -1464,10 +1505,6 @@
|
|
|
1464
1505
|
border-style: dashed;
|
|
1465
1506
|
}
|
|
1466
1507
|
|
|
1467
|
-
.border-action {
|
|
1468
|
-
border-color: var(--action);
|
|
1469
|
-
}
|
|
1470
|
-
|
|
1471
1508
|
.border-action-line {
|
|
1472
1509
|
border-color: var(--action-line);
|
|
1473
1510
|
}
|
|
@@ -1677,6 +1714,10 @@
|
|
|
1677
1714
|
padding: var(--spacing);
|
|
1678
1715
|
}
|
|
1679
1716
|
|
|
1717
|
+
.p-1\.5 {
|
|
1718
|
+
padding: calc(var(--spacing) * 1.5);
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1680
1721
|
.p-2 {
|
|
1681
1722
|
padding: calc(var(--spacing) * 2);
|
|
1682
1723
|
}
|
|
@@ -2204,11 +2245,24 @@
|
|
|
2204
2245
|
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
|
2205
2246
|
}
|
|
2206
2247
|
|
|
2207
|
-
.ring {
|
|
2208
|
-
--tw-ring-shadow: var(--tw-ring-inset, ) 0 0 0 calc(
|
|
2248
|
+
.ring-2 {
|
|
2249
|
+
--tw-ring-shadow: var(--tw-ring-inset, ) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
|
|
2209
2250
|
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
|
2210
2251
|
}
|
|
2211
2252
|
|
|
2253
|
+
.ring-action {
|
|
2254
|
+
--tw-ring-color: var(--action);
|
|
2255
|
+
}
|
|
2256
|
+
|
|
2257
|
+
.ring-offset-2 {
|
|
2258
|
+
--tw-ring-offset-width: 2px;
|
|
2259
|
+
--tw-ring-offset-shadow: var(--tw-ring-inset, ) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
|
2260
|
+
}
|
|
2261
|
+
|
|
2262
|
+
.ring-offset-surface {
|
|
2263
|
+
--tw-ring-offset-color: var(--surface);
|
|
2264
|
+
}
|
|
2265
|
+
|
|
2212
2266
|
.outline {
|
|
2213
2267
|
outline-style: var(--tw-outline-style);
|
|
2214
2268
|
outline-width: 1px;
|
|
@@ -2404,6 +2458,10 @@
|
|
|
2404
2458
|
outline-offset: -2px;
|
|
2405
2459
|
}
|
|
2406
2460
|
|
|
2461
|
+
.active\:scale-\[0\.94\]:active {
|
|
2462
|
+
scale: .94;
|
|
2463
|
+
}
|
|
2464
|
+
|
|
2407
2465
|
.active\:bg-soft2:active {
|
|
2408
2466
|
background-color: var(--soft2);
|
|
2409
2467
|
}
|
|
@@ -2858,6 +2916,10 @@
|
|
|
2858
2916
|
--grid-dot: #c9ced5;
|
|
2859
2917
|
--note: #fdf3e3;
|
|
2860
2918
|
--note-line: #edd6b1;
|
|
2919
|
+
--draw-ink: #1b1f24;
|
|
2920
|
+
--draw-red: #e03131;
|
|
2921
|
+
--draw-blue: #1971c2;
|
|
2922
|
+
--draw-green: #2b8a3e;
|
|
2861
2923
|
--scrollbar: #c3c9d0;
|
|
2862
2924
|
--scrollbar-hover: #9aa2ac;
|
|
2863
2925
|
color-scheme: light;
|
|
@@ -2892,6 +2954,10 @@
|
|
|
2892
2954
|
--grid-dot: #30343a;
|
|
2893
2955
|
--note: #2a2415;
|
|
2894
2956
|
--note-line: #574420;
|
|
2957
|
+
--draw-ink: #e6e8eb;
|
|
2958
|
+
--draw-red: #ff6b6b;
|
|
2959
|
+
--draw-blue: #4dabf7;
|
|
2960
|
+
--draw-green: #51cf66;
|
|
2895
2961
|
--scrollbar: #3a3f46;
|
|
2896
2962
|
--scrollbar-hover: #565d66;
|
|
2897
2963
|
color-scheme: dark;
|
|
@@ -3121,6 +3187,36 @@
|
|
|
3121
3187
|
background-color: var(--bg);
|
|
3122
3188
|
}
|
|
3123
3189
|
|
|
3190
|
+
@keyframes target-ripple {
|
|
3191
|
+
0% {
|
|
3192
|
+
box-shadow: 0 0 0 0 color-mix(in srgb, var(--ripple-color, var(--action)) 70%, transparent), 0 0 0 0 transparent;
|
|
3193
|
+
}
|
|
3194
|
+
|
|
3195
|
+
20% {
|
|
3196
|
+
box-shadow: 0 0 0 8px color-mix(in srgb, var(--ripple-color, var(--action)) 32%, transparent), 0 0 0 0 color-mix(in srgb, var(--ripple-color, var(--action)) 55%, transparent);
|
|
3197
|
+
}
|
|
3198
|
+
|
|
3199
|
+
45% {
|
|
3200
|
+
box-shadow: 0 0 0 16px transparent, 0 0 0 8px color-mix(in srgb, var(--ripple-color, var(--action)) 24%, transparent);
|
|
3201
|
+
}
|
|
3202
|
+
|
|
3203
|
+
60%, 100% {
|
|
3204
|
+
box-shadow: 0 0 0 20px #0000, 0 0 0 16px #0000;
|
|
3205
|
+
}
|
|
3206
|
+
}
|
|
3207
|
+
|
|
3208
|
+
.target-ripple {
|
|
3209
|
+
isolation: isolate;
|
|
3210
|
+
animation: 2s cubic-bezier(.16, 1, .3, 1) .16s both target-ripple;
|
|
3211
|
+
}
|
|
3212
|
+
|
|
3213
|
+
@media (prefers-reduced-motion: reduce) {
|
|
3214
|
+
.target-ripple {
|
|
3215
|
+
box-shadow: 0 0 0 2px var(--ripple-color, var(--action));
|
|
3216
|
+
animation: none;
|
|
3217
|
+
}
|
|
3218
|
+
}
|
|
3219
|
+
|
|
3124
3220
|
.canvas-grid {
|
|
3125
3221
|
pointer-events: none;
|
|
3126
3222
|
background-image: radial-gradient(circle, var(--grid-dot) 1px, transparent 1.2px);
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drawings: an idea board for each canvas page and markup over each screen. They are not screens:
|
|
3
|
+
* people draw them in the viewer, agents read them, and in the cloud every member sees the same
|
|
4
|
+
* ones as they are drawn. A drawing is a set of strokes plus the ids of erased ones, so two copies
|
|
5
|
+
* merge by union whatever order their changes arrive in.
|
|
6
|
+
*/
|
|
7
|
+
/** `board:<page id>` or `markup:<item id>`. */
|
|
8
|
+
export type DrawingKey = string;
|
|
9
|
+
export declare const INKS: readonly ['ink', 'red', 'blue', 'green'];
|
|
10
|
+
export type Ink = (typeof INKS)[number];
|
|
11
|
+
export declare const STROKE_KINDS: readonly ['pen', 'arrow', 'rect', 'text'];
|
|
12
|
+
export type StrokeKind = (typeof STROKE_KINDS)[number];
|
|
13
|
+
export interface Stroke {
|
|
14
|
+
id: string;
|
|
15
|
+
kind: StrokeKind;
|
|
16
|
+
ink: Ink;
|
|
17
|
+
/** Line width in drawing pixels; the font size for text. */
|
|
18
|
+
size: number;
|
|
19
|
+
/** pen: x, y, pressure triples; arrow and rect: x1, y1, x2, y2; text: x, y of its top left. */
|
|
20
|
+
points: number[];
|
|
21
|
+
text?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface Drawing {
|
|
24
|
+
strokes: Stroke[];
|
|
25
|
+
/** Erased strokes stay erased, even when a copy that still has them merges in later. */
|
|
26
|
+
erased: string[];
|
|
27
|
+
}
|
|
28
|
+
export interface DrawingOps {
|
|
29
|
+
add?: Stroke[];
|
|
30
|
+
erase?: string[];
|
|
31
|
+
}
|
|
32
|
+
export declare const MAX_STROKES = 4000;
|
|
33
|
+
/** Longest message either side takes. */
|
|
34
|
+
export declare const MAX_MESSAGE: number;
|
|
35
|
+
export declare const boardKey: (page: string) => DrawingKey;
|
|
36
|
+
export declare const markupKey: (item: string) => DrawingKey;
|
|
37
|
+
export declare const isDrawingKey: (value: unknown) => value is DrawingKey;
|
|
38
|
+
export declare const emptyDrawing: () => Drawing;
|
|
39
|
+
/** A random stroke id. */
|
|
40
|
+
export declare function strokeId(): string;
|
|
41
|
+
export declare function parseStroke(value: unknown): Stroke | null;
|
|
42
|
+
export declare function parseOps(value: unknown): DrawingOps | null;
|
|
43
|
+
export declare function parseDrawing(value: unknown): Drawing | null;
|
|
44
|
+
/** `{ [key]: Drawing }`, dropping what does not parse. */
|
|
45
|
+
export declare function parseDrawings(value: unknown): Record<DrawingKey, Drawing>;
|
|
46
|
+
/** `drawing` with `ops` applied, or null when they change nothing. Strokes past MAX_STROKES are not added. */
|
|
47
|
+
export declare function applyOps(drawing: Drawing, ops: DrawingOps): Drawing | null;
|
|
48
|
+
/** What `from` has that `to` lacks, as ops that bring `to` up to it. */
|
|
49
|
+
export declare function diffDrawings(from: Drawing, to: Drawing): DrawingOps | null;
|
|
50
|
+
export declare function isEmptyDrawing(drawing: Drawing | undefined): boolean;
|
|
51
|
+
export interface Box {
|
|
52
|
+
x: number;
|
|
53
|
+
y: number;
|
|
54
|
+
w: number;
|
|
55
|
+
h: number;
|
|
56
|
+
}
|
|
57
|
+
/** Text lines are this many font sizes apart. */
|
|
58
|
+
export declare const TEXT_LINE_HEIGHT = 1.25;
|
|
59
|
+
/** The box a text stroke takes, roughly (no font metrics here): for erasing, cropping and its editor. */
|
|
60
|
+
export declare function textBox(stroke: Stroke): Box;
|
|
61
|
+
/** The box a stroke takes, its line width, arrowhead and text included. */
|
|
62
|
+
export declare function strokeBounds(stroke: Stroke): Box;
|
|
63
|
+
/** The box all the strokes take; null for none. */
|
|
64
|
+
export declare function drawingBounds(strokes: Stroke[]): Box | null;
|
|
65
|
+
/** Strokes closer than this belong to one idea on a board. */
|
|
66
|
+
export declare const GROUP_GAP = 240;
|
|
67
|
+
/**
|
|
68
|
+
* A board's strokes in groups: what is drawn close together (less than GROUP_GAP apart) is one
|
|
69
|
+
* group, empty space between sketches parts them. In reading order: rows top to bottom, then left
|
|
70
|
+
* to right. A picture of a board is one per group, so far-apart ideas do not shrink into specks.
|
|
71
|
+
*/
|
|
72
|
+
export declare function drawingGroups(strokes: Stroke[]): {
|
|
73
|
+
box: Box;
|
|
74
|
+
strokes: Stroke[];
|
|
75
|
+
}[];
|
|
76
|
+
/**
|
|
77
|
+
* A stroke on its way, sent every few frames while it is drawn and not kept: `points` continue the
|
|
78
|
+
* stroke's points from index `at` (0 starts it over, as a shape does on every move).
|
|
79
|
+
*/
|
|
80
|
+
export interface LiveStroke {
|
|
81
|
+
id: string;
|
|
82
|
+
kind: StrokeKind;
|
|
83
|
+
ink: Ink;
|
|
84
|
+
size: number;
|
|
85
|
+
text?: string;
|
|
86
|
+
at: number;
|
|
87
|
+
points: number[];
|
|
88
|
+
}
|
|
89
|
+
/** Messages to the server over the drawings socket of one canvas. */
|
|
90
|
+
export type ClientMessage = {
|
|
91
|
+
type: 'ops';
|
|
92
|
+
id: number;
|
|
93
|
+
key: DrawingKey;
|
|
94
|
+
add?: Stroke[];
|
|
95
|
+
erase?: string[];
|
|
96
|
+
} | {
|
|
97
|
+
type: 'live';
|
|
98
|
+
key: DrawingKey;
|
|
99
|
+
stroke: LiveStroke;
|
|
100
|
+
} | {
|
|
101
|
+
type: 'live-end';
|
|
102
|
+
key: DrawingKey;
|
|
103
|
+
id: string;
|
|
104
|
+
};
|
|
105
|
+
/** Messages from the server. */
|
|
106
|
+
export type ServerMessage =
|
|
107
|
+
/** First thing on a connection: every drawing of the canvas, and whether this peer may draw. */
|
|
108
|
+
{
|
|
109
|
+
type: 'hello';
|
|
110
|
+
peer: string;
|
|
111
|
+
canDraw: boolean;
|
|
112
|
+
drawings: Record<DrawingKey, Drawing>;
|
|
113
|
+
}
|
|
114
|
+
/** Someone's change, this peer's own included. */
|
|
115
|
+
| {
|
|
116
|
+
type: 'ops';
|
|
117
|
+
key: DrawingKey;
|
|
118
|
+
add?: Stroke[];
|
|
119
|
+
erase?: string[];
|
|
120
|
+
peer: string;
|
|
121
|
+
}
|
|
122
|
+
/** The server kept the ops the client numbered `id`. */
|
|
123
|
+
| {
|
|
124
|
+
type: 'ack';
|
|
125
|
+
id: number;
|
|
126
|
+
}
|
|
127
|
+
/** The whole drawing, where a change came some other way than ops (a file edited here, another server). */
|
|
128
|
+
| {
|
|
129
|
+
type: 'doc';
|
|
130
|
+
key: DrawingKey;
|
|
131
|
+
drawing: Drawing;
|
|
132
|
+
} | {
|
|
133
|
+
type: 'live';
|
|
134
|
+
key: DrawingKey;
|
|
135
|
+
stroke: LiveStroke;
|
|
136
|
+
peer: string;
|
|
137
|
+
name?: string;
|
|
138
|
+
} | {
|
|
139
|
+
type: 'live-end';
|
|
140
|
+
key: DrawingKey;
|
|
141
|
+
id: string;
|
|
142
|
+
peer: string;
|
|
143
|
+
}
|
|
144
|
+
/** A peer left: its strokes on the way go with it. */
|
|
145
|
+
| {
|
|
146
|
+
type: 'gone';
|
|
147
|
+
peer: string;
|
|
148
|
+
} | {
|
|
149
|
+
type: 'error';
|
|
150
|
+
message: string;
|
|
151
|
+
};
|
|
152
|
+
export declare function parseClientMessage(raw: string): ClientMessage | null;
|
|
153
|
+
export declare function parseServerMessage(raw: string): ServerMessage | null;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { type Drawing, type DrawingKey, type DrawingOps, type Stroke } from '../../../src/shared/drawings';
|
|
2
|
+
import { Store } from './store';
|
|
3
|
+
/** A stroke someone else is drawing right now. */
|
|
4
|
+
export interface Ghost {
|
|
5
|
+
peer: string;
|
|
6
|
+
name?: string;
|
|
7
|
+
stroke: Stroke;
|
|
8
|
+
seen: number;
|
|
9
|
+
}
|
|
10
|
+
export interface DrawingsState {
|
|
11
|
+
/** The server said hello: the drawings are the real ones. */
|
|
12
|
+
ready: boolean;
|
|
13
|
+
online: boolean;
|
|
14
|
+
canDraw: boolean;
|
|
15
|
+
drawings: Record<DrawingKey, Drawing>;
|
|
16
|
+
ghosts: Record<DrawingKey, Ghost[]>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The drawings of one canvas over its socket. What shows is the server's drawings with this user's
|
|
20
|
+
* changes it has not acknowledged yet on top: they show at once, go out numbered, and go out again
|
|
21
|
+
* after a reconnect (applying them twice changes nothing). A change the server refuses comes back
|
|
22
|
+
* as the stored drawing, then the acknowledgement, and so drops out of view.
|
|
23
|
+
*/
|
|
24
|
+
export declare class DrawingClient {
|
|
25
|
+
private readonly url;
|
|
26
|
+
readonly store: Store<DrawingsState>;
|
|
27
|
+
private socket;
|
|
28
|
+
/** The server's drawings, as far as it told. */
|
|
29
|
+
private confirmed;
|
|
30
|
+
private pending;
|
|
31
|
+
private nextId;
|
|
32
|
+
private delay;
|
|
33
|
+
private timer;
|
|
34
|
+
private sweep;
|
|
35
|
+
private closed;
|
|
36
|
+
constructor(url: string);
|
|
37
|
+
private connect;
|
|
38
|
+
private retry;
|
|
39
|
+
private send;
|
|
40
|
+
/** The drawing to show: the server's, with the changes still on their way applied. */
|
|
41
|
+
private shown;
|
|
42
|
+
private show;
|
|
43
|
+
private receive;
|
|
44
|
+
private dropGhosts;
|
|
45
|
+
private dropStale;
|
|
46
|
+
/** A change of this user's: shown at once, sent, and sent again after a reconnect until acknowledged. */
|
|
47
|
+
apply(key: DrawingKey, ops: DrawingOps): void;
|
|
48
|
+
/** A stroke on its way: its points from `at` on (0 sends it whole). */
|
|
49
|
+
live(key: DrawingKey, stroke: Stroke, at: number): void;
|
|
50
|
+
liveEnd(key: DrawingKey, id: string): void;
|
|
51
|
+
close(): void;
|
|
52
|
+
}
|
|
53
|
+
/** The drawings of a canvas while it is open, or null when the source has none. */
|
|
54
|
+
export declare function useDrawingClient(canvas: string, enabled?: boolean): DrawingClient | null;
|
|
55
|
+
/** One drawing and the strokes on their way onto it. */
|
|
56
|
+
export declare function useDrawing(client: DrawingClient, key: DrawingKey): {
|
|
57
|
+
drawing: Drawing | undefined;
|
|
58
|
+
ghosts: Ghost[];
|
|
59
|
+
};
|
|
60
|
+
/** For hooks that need a store before the canvas's drawings are there. */
|
|
61
|
+
export declare const noDrawings: Store<DrawingsState>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Draws the eye to `target` with two rings spreading out from its edge (as Gateway does for the
|
|
3
|
+
* section a link led to): after scrolling to it, or to a notice that must not go unseen. The rings
|
|
4
|
+
* are the element's own box-shadow, following its border radius, so give it to an element without a
|
|
5
|
+
* shadow of its own that nothing clips. Calling it again starts it over.
|
|
6
|
+
*/
|
|
7
|
+
export declare function ripple(target: HTMLElement, color?: string): void;
|
|
@@ -29,6 +29,12 @@ export interface ViewerSource {
|
|
|
29
29
|
source(path: string, unit: string): Promise<string>;
|
|
30
30
|
/** Set the project icon (an SVG, PNG or WebP), or remove it with null. Without it the viewer offers no icon controls. */
|
|
31
31
|
setIcon?(file: Blob | null): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* The WebSocket URL of a canvas's drawings: the idea board of each page and the markup over each
|
|
34
|
+
* screen, live for everyone who has the canvas open (`src/shared/drawings.ts` holds the protocol).
|
|
35
|
+
* Without it the viewer shows no drawings.
|
|
36
|
+
*/
|
|
37
|
+
drawings?(canvas: string): string;
|
|
32
38
|
}
|
|
33
39
|
export declare class ApiError extends Error {
|
|
34
40
|
readonly status: number;
|
|
@@ -9,6 +9,7 @@ export { cn } from './lib/cn';
|
|
|
9
9
|
export { copyText } from './lib/copy';
|
|
10
10
|
export { followLink, frameSrc, type LinkTarget, listenToFrame, sendTheme } from './lib/frames';
|
|
11
11
|
export { useIsPhone } from './lib/phone';
|
|
12
|
+
export { ripple } from './lib/ripple';
|
|
12
13
|
export { ApiError, localSource, resolveUrl, staticSource, type ViewerSource } from './lib/source';
|
|
13
14
|
export { setThemePref, type ThemePref, useTheme, useThemePref } from './lib/theme';
|
|
14
15
|
export type { ViewerScope, ViewerSlots } from './lib/viewer';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { CanvasPage } from '../../../../src/shared/types';
|
|
2
|
+
import { type DrawingClient } from '../../lib/drawings';
|
|
3
|
+
import { type ToolSizes } from './drawing/editor';
|
|
4
|
+
export declare const BOARD_SIZES: ToolSizes;
|
|
5
|
+
/**
|
|
6
|
+
* The page's idea board: a tile above the zoom controls with what is drawn on it, which opens over
|
|
7
|
+
* the canvas. The board has no edges: it pans and zooms like the canvas, and a picture of it is
|
|
8
|
+
* cropped to what is drawn. Whether it is open, and where it looks, is kept per page for the
|
|
9
|
+
* session. Everyone with the canvas open sees it change as it is drawn on; those who may not draw
|
|
10
|
+
* see it only once there is something on it.
|
|
11
|
+
*/
|
|
12
|
+
export declare function IdeaBoard({ client, canvasId, page }: {
|
|
13
|
+
client: DrawingClient;
|
|
14
|
+
canvasId: string;
|
|
15
|
+
page: CanvasPage;
|
|
16
|
+
}): import("react").JSX.Element | null;
|
|
@@ -34,10 +34,17 @@ export declare class CameraStore {
|
|
|
34
34
|
y: number;
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
|
+
/** A camera saved in the session under `key`, or null. */
|
|
38
|
+
export declare function loadCamera(key: string): Camera | null;
|
|
37
39
|
/**
|
|
38
40
|
* The camera that shows `rect` inside a viewport of `w`×`h` with `pad` screen pixels around it.
|
|
39
41
|
* When `rect` does not fit even at `minZoom`, it lines up with the top (and the left, if it is too wide).
|
|
40
42
|
*/
|
|
41
43
|
export declare function fitRect(rect: Rect, w: number, h: number, pad?: number, maxZoom?: number, minZoom?: number): Camera;
|
|
44
|
+
/**
|
|
45
|
+
* Keeps a `.canvas-grid` layer in step with a camera. The dot grid is a layer of its own: panning
|
|
46
|
+
* only moves it, zooming repaints it.
|
|
47
|
+
*/
|
|
48
|
+
export declare function gridMover(grid: HTMLElement): (c: Camera) => void;
|
|
42
49
|
export declare function intersects(a: Rect, b: Rect): boolean;
|
|
43
50
|
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CanvasDoc, CanvasItem, CanvasPage as Page, Theme } from '../../../../src/shared/types';
|
|
2
|
+
import { type ReactNode } from 'react';
|
|
2
3
|
import type { ViewStore } from './view-state';
|
|
3
4
|
/** The canvas page's top bar: the canvas and its pages, screen theme, inspect and play. */
|
|
4
5
|
export declare function CanvasBar({ canvas, page, theme, onTheme, sidebar, onSidebar, onPlay, canPlay, inspect, onInspect, }: {
|
|
@@ -13,14 +14,24 @@ export declare function CanvasBar({ canvas, page, theme, onTheme, sidebar, onSid
|
|
|
13
14
|
inspect: boolean;
|
|
14
15
|
onInspect(value: boolean): void;
|
|
15
16
|
}): import("react").JSX.Element;
|
|
17
|
+
/**
|
|
18
|
+
* The strip along the canvas's bottom edge that holds a bar: centred while there is room, moved aside
|
|
19
|
+
* when there is not, and never under the controls in the corners, whose widths come in as
|
|
20
|
+
* `--dock-left` and `--dock-right` (see useDock).
|
|
21
|
+
*/
|
|
22
|
+
export declare function BottomDock({ children }: {
|
|
23
|
+
children: ReactNode;
|
|
24
|
+
}): import("react").JSX.Element;
|
|
16
25
|
/** The selected item's name and actions, floating over the bottom of the canvas; it eases in and out. */
|
|
17
|
-
export declare function SelectionBar({ canvas, store, layoutItems, theme, onPlay, inspect, }: {
|
|
26
|
+
export declare function SelectionBar({ canvas, store, layoutItems, theme, onPlay, inspect, onMarkup, }: {
|
|
18
27
|
canvas: CanvasDoc;
|
|
19
28
|
store: ViewStore;
|
|
20
29
|
layoutItems: CanvasItem[];
|
|
21
30
|
theme: Theme;
|
|
22
31
|
onPlay(id: string): void;
|
|
23
32
|
inspect: boolean;
|
|
33
|
+
/** Mark up a screen; without it, where no one may draw, there is no such button. */
|
|
34
|
+
onMarkup?: (id: string) => void;
|
|
24
35
|
}): import("react").JSX.Element;
|
|
25
36
|
/** The inspect mode switch: a button that stays pressed while on. */
|
|
26
37
|
export declare function InspectToggle({ on, onChange, compact, }: {
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { type DrawingKey, type Ink, type Stroke } from '../../../../../src/shared/drawings';
|
|
2
|
+
import { type ReactNode } from 'react';
|
|
3
|
+
import { type DrawingClient } from '../../../lib/drawings';
|
|
4
|
+
import type { CameraStore } from '../camera';
|
|
5
|
+
export type Tool = 'pen' | 'arrow' | 'rect' | 'text' | 'eraser';
|
|
6
|
+
/** Line widths and text size, in the drawing's own pixels. */
|
|
7
|
+
export interface ToolSizes {
|
|
8
|
+
pen: number;
|
|
9
|
+
line: number;
|
|
10
|
+
text: number;
|
|
11
|
+
}
|
|
12
|
+
type Action = {
|
|
13
|
+
kind: 'add' | 'erase';
|
|
14
|
+
strokes: Stroke[];
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* One drawing's tools and this user's history on it; null while there is no drawing to edit.
|
|
18
|
+
* Undoing brings strokes back as new ones: an erased id stays erased everywhere.
|
|
19
|
+
*/
|
|
20
|
+
export declare function useDrawingEditor(client: DrawingClient | null, key: DrawingKey | null, defaultInk?: Ink): {
|
|
21
|
+
client: DrawingClient;
|
|
22
|
+
drawingKey: string;
|
|
23
|
+
tool: Tool;
|
|
24
|
+
setTool: import("react").Dispatch<import("react").SetStateAction<Tool>>;
|
|
25
|
+
ink: "blue" | "green" | "ink" | "red";
|
|
26
|
+
setInk: import("react").Dispatch<import("react").SetStateAction<"blue" | "green" | "ink" | "red">>;
|
|
27
|
+
record: (action: Action) => void;
|
|
28
|
+
eraseNow: (strokes: Stroke[]) => void;
|
|
29
|
+
add(strokes: Stroke[]): void;
|
|
30
|
+
clear(): void;
|
|
31
|
+
undo: () => void;
|
|
32
|
+
redo: () => void;
|
|
33
|
+
canUndo: boolean;
|
|
34
|
+
canRedo: boolean;
|
|
35
|
+
} | null;
|
|
36
|
+
export type DrawingEditor = NonNullable<ReturnType<typeof useDrawingEditor>>;
|
|
37
|
+
/** Space held: a press on a drawing pans instead of drawing, as it does on the canvas. */
|
|
38
|
+
export declare function useSpaceHeld(): import("react").RefObject<boolean>;
|
|
39
|
+
/** Tool keys, 1–4 for inks, ⌘Z and ⇧⌘Z while `active`; typing in a field is left alone. */
|
|
40
|
+
export declare function useEditorKeys(editor: DrawingEditor | null, active: boolean): void;
|
|
41
|
+
/**
|
|
42
|
+
* Tools, inks, undo and redo, clearing, and whatever the place adds at the end (a way out). Short
|
|
43
|
+
* of room (`compact`), the inks fold into one button that picks among them.
|
|
44
|
+
*/
|
|
45
|
+
export declare function DrawingToolbar({ editor, end, compact, }: {
|
|
46
|
+
editor: DrawingEditor;
|
|
47
|
+
end?: ReactNode;
|
|
48
|
+
compact?: boolean;
|
|
49
|
+
}): import("react").JSX.Element;
|
|
50
|
+
/**
|
|
51
|
+
* A drawing as SVG in its own coordinates (`width` × `height`), fitted to its box. With an editor
|
|
52
|
+
* it takes the pointer and draws with the editor's tool; strokes on their way go out live.
|
|
53
|
+
*/
|
|
54
|
+
export declare function DrawingSurface({ client, drawingKey, width, height, editor, sizes, className, camera, onPointerDownCapture, }: {
|
|
55
|
+
client: DrawingClient;
|
|
56
|
+
drawingKey: DrawingKey;
|
|
57
|
+
/** The drawing's size, when it has one (a screen's markup); a board has a camera instead. */
|
|
58
|
+
width?: number;
|
|
59
|
+
height?: number;
|
|
60
|
+
editor?: DrawingEditor | null;
|
|
61
|
+
sizes: ToolSizes;
|
|
62
|
+
className?: string;
|
|
63
|
+
/** An endless drawing (a board): what shows is where this camera looks. */
|
|
64
|
+
camera?: CameraStore;
|
|
65
|
+
/** Before the surface acts on a press: return true to leave it to what is under the surface (a pan). */
|
|
66
|
+
onPointerDownCapture?: (e: React.PointerEvent) => boolean;
|
|
67
|
+
}): import("react").JSX.Element;
|
|
68
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type Drawing, type Ink, type Stroke } from '../../../../../src/shared/drawings';
|
|
2
|
+
import type { Ghost } from '../../../lib/drawings';
|
|
3
|
+
export declare const INK_COLOR: Record<Ink, string>;
|
|
4
|
+
export declare const StrokeView: import("react").MemoExoticComponent<({ stroke, done }: {
|
|
5
|
+
stroke: Stroke;
|
|
6
|
+
done?: boolean;
|
|
7
|
+
}) => import("react").JSX.Element>;
|
|
8
|
+
/** The strokes of a drawing, then those on their way (someone else's, and this user's own). */
|
|
9
|
+
export declare function StrokeList({ drawing, ghosts, draft, }: {
|
|
10
|
+
drawing: Drawing | undefined;
|
|
11
|
+
ghosts?: Ghost[];
|
|
12
|
+
draft?: Stroke | null;
|
|
13
|
+
}): import("react").JSX.Element;
|
|
14
|
+
/** How far `(x, y)` is from the stroke's line, in drawing pixels (0 inside a text). */
|
|
15
|
+
export declare function distanceTo(stroke: Stroke, x: number, y: number): number;
|