foss-design 0.9.12 → 0.9.13
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-BOLNePag.css +2 -0
- package/dist/viewer/assets/index-BrtsfBgT.js +163 -0
- package/dist/viewer/index.html +2 -2
- package/dist/viewer-lib/index.js +3588 -1356
- package/dist/viewer-lib/styles.css +76 -4
- 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/source.d.ts +6 -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
|
@@ -466,10 +466,18 @@
|
|
|
466
466
|
inset: 0;
|
|
467
467
|
}
|
|
468
468
|
|
|
469
|
+
.inset-4 {
|
|
470
|
+
inset: calc(var(--spacing) * 4);
|
|
471
|
+
}
|
|
472
|
+
|
|
469
473
|
.inset-x-0 {
|
|
470
474
|
inset-inline: 0;
|
|
471
475
|
}
|
|
472
476
|
|
|
477
|
+
.inset-x-4 {
|
|
478
|
+
inset-inline: calc(var(--spacing) * 4);
|
|
479
|
+
}
|
|
480
|
+
|
|
473
481
|
.inset-x-5 {
|
|
474
482
|
inset-inline: calc(var(--spacing) * 5);
|
|
475
483
|
}
|
|
@@ -534,6 +542,10 @@
|
|
|
534
542
|
bottom: calc(var(--spacing) * 5);
|
|
535
543
|
}
|
|
536
544
|
|
|
545
|
+
.bottom-\[76px\] {
|
|
546
|
+
bottom: 76px;
|
|
547
|
+
}
|
|
548
|
+
|
|
537
549
|
.bottom-full {
|
|
538
550
|
bottom: 100%;
|
|
539
551
|
}
|
|
@@ -739,11 +751,21 @@
|
|
|
739
751
|
height: 7px;
|
|
740
752
|
}
|
|
741
753
|
|
|
754
|
+
.size-\[12px\] {
|
|
755
|
+
width: 12px;
|
|
756
|
+
height: 12px;
|
|
757
|
+
}
|
|
758
|
+
|
|
742
759
|
.size-\[14px\] {
|
|
743
760
|
width: 14px;
|
|
744
761
|
height: 14px;
|
|
745
762
|
}
|
|
746
763
|
|
|
764
|
+
.size-\[92px\] {
|
|
765
|
+
width: 92px;
|
|
766
|
+
height: 92px;
|
|
767
|
+
}
|
|
768
|
+
|
|
747
769
|
.size-full {
|
|
748
770
|
width: 100%;
|
|
749
771
|
height: 100%;
|
|
@@ -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
|
}
|
|
@@ -1149,6 +1171,10 @@
|
|
|
1149
1171
|
flex-basis: 0;
|
|
1150
1172
|
}
|
|
1151
1173
|
|
|
1174
|
+
.origin-top-left {
|
|
1175
|
+
transform-origin: 0 0;
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1152
1178
|
.-translate-x-1\/2 {
|
|
1153
1179
|
--tw-translate-x: calc(calc(1 / 2 * 100%) * -1);
|
|
1154
1180
|
translate: var(--tw-translate-x) var(--tw-translate-y);
|
|
@@ -1211,6 +1237,10 @@
|
|
|
1211
1237
|
resize: both;
|
|
1212
1238
|
}
|
|
1213
1239
|
|
|
1240
|
+
.resize-none {
|
|
1241
|
+
resize: none;
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1214
1244
|
.resize-y {
|
|
1215
1245
|
resize: vertical;
|
|
1216
1246
|
}
|
|
@@ -1223,6 +1253,10 @@
|
|
|
1223
1253
|
grid-template-columns: 92px minmax(0, 1fr);
|
|
1224
1254
|
}
|
|
1225
1255
|
|
|
1256
|
+
.grid-cols-\[minmax\(var\(--dock-left\,0px\)\,1fr\)_minmax\(0\,auto\)_minmax\(var\(--dock-right\,0px\)\,1fr\)\] {
|
|
1257
|
+
grid-template-columns: minmax(var(--dock-left, 0px),1fr) minmax(0,auto) minmax(var(--dock-right, 0px),1fr);
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1226
1260
|
.grid-rows-\[20px_auto_20px\] {
|
|
1227
1261
|
grid-template-rows: 20px auto 20px;
|
|
1228
1262
|
}
|
|
@@ -1353,6 +1387,10 @@
|
|
|
1353
1387
|
overflow: hidden;
|
|
1354
1388
|
}
|
|
1355
1389
|
|
|
1390
|
+
.overflow-visible {
|
|
1391
|
+
overflow: visible;
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1356
1394
|
.overflow-x-auto {
|
|
1357
1395
|
overflow-x: auto;
|
|
1358
1396
|
}
|
|
@@ -1677,6 +1715,10 @@
|
|
|
1677
1715
|
padding: var(--spacing);
|
|
1678
1716
|
}
|
|
1679
1717
|
|
|
1718
|
+
.p-1\.5 {
|
|
1719
|
+
padding: calc(var(--spacing) * 1.5);
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1680
1722
|
.p-2 {
|
|
1681
1723
|
padding: calc(var(--spacing) * 2);
|
|
1682
1724
|
}
|
|
@@ -2209,6 +2251,24 @@
|
|
|
2209
2251
|
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
|
2210
2252
|
}
|
|
2211
2253
|
|
|
2254
|
+
.ring-2 {
|
|
2255
|
+
--tw-ring-shadow: var(--tw-ring-inset, ) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
|
|
2256
|
+
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
|
2257
|
+
}
|
|
2258
|
+
|
|
2259
|
+
.ring-action {
|
|
2260
|
+
--tw-ring-color: var(--action);
|
|
2261
|
+
}
|
|
2262
|
+
|
|
2263
|
+
.ring-offset-2 {
|
|
2264
|
+
--tw-ring-offset-width: 2px;
|
|
2265
|
+
--tw-ring-offset-shadow: var(--tw-ring-inset, ) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
|
2266
|
+
}
|
|
2267
|
+
|
|
2268
|
+
.ring-offset-surface {
|
|
2269
|
+
--tw-ring-offset-color: var(--surface);
|
|
2270
|
+
}
|
|
2271
|
+
|
|
2212
2272
|
.outline {
|
|
2213
2273
|
outline-style: var(--tw-outline-style);
|
|
2214
2274
|
outline-width: 1px;
|
|
@@ -2404,6 +2464,10 @@
|
|
|
2404
2464
|
outline-offset: -2px;
|
|
2405
2465
|
}
|
|
2406
2466
|
|
|
2467
|
+
.active\:scale-\[0\.94\]:active {
|
|
2468
|
+
scale: .94;
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2407
2471
|
.active\:bg-soft2:active {
|
|
2408
2472
|
background-color: var(--soft2);
|
|
2409
2473
|
}
|
|
@@ -2858,6 +2922,10 @@
|
|
|
2858
2922
|
--grid-dot: #c9ced5;
|
|
2859
2923
|
--note: #fdf3e3;
|
|
2860
2924
|
--note-line: #edd6b1;
|
|
2925
|
+
--draw-ink: #1b1f24;
|
|
2926
|
+
--draw-red: #e03131;
|
|
2927
|
+
--draw-blue: #1971c2;
|
|
2928
|
+
--draw-green: #2b8a3e;
|
|
2861
2929
|
--scrollbar: #c3c9d0;
|
|
2862
2930
|
--scrollbar-hover: #9aa2ac;
|
|
2863
2931
|
color-scheme: light;
|
|
@@ -2892,6 +2960,10 @@
|
|
|
2892
2960
|
--grid-dot: #30343a;
|
|
2893
2961
|
--note: #2a2415;
|
|
2894
2962
|
--note-line: #574420;
|
|
2963
|
+
--draw-ink: #e6e8eb;
|
|
2964
|
+
--draw-red: #ff6b6b;
|
|
2965
|
+
--draw-blue: #4dabf7;
|
|
2966
|
+
--draw-green: #51cf66;
|
|
2895
2967
|
--scrollbar: #3a3f46;
|
|
2896
2968
|
--scrollbar-hover: #565d66;
|
|
2897
2969
|
color-scheme: dark;
|
|
@@ -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>;
|
|
@@ -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;
|
|
@@ -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;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
declare global {
|
|
2
|
+
interface Window {
|
|
3
|
+
__DESIGN_DRAWING_READY__?: boolean;
|
|
4
|
+
__DESIGN_DRAWING_SIZE__?: {
|
|
5
|
+
width: number;
|
|
6
|
+
height: number;
|
|
7
|
+
};
|
|
8
|
+
__DESIGN_DRAWING_PARTS__?: number;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export declare function BoardCapture({ canvasId, pageId }: {
|
|
12
|
+
canvasId: string;
|
|
13
|
+
pageId: string;
|
|
14
|
+
}): import("react").JSX.Element | null;
|
|
15
|
+
export declare function MarkupCapture({ canvasId, itemId }: {
|
|
16
|
+
canvasId: string;
|
|
17
|
+
itemId: string;
|
|
18
|
+
}): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { CanvasItem } from '../../../../src/shared/types';
|
|
2
|
+
import type { DrawingClient } from '../../lib/drawings';
|
|
3
|
+
import { type DrawingEditor, type ToolSizes } from './drawing/editor';
|
|
4
|
+
import type { Layout } from './layout';
|
|
5
|
+
import type { ViewStore } from './view-state';
|
|
6
|
+
export declare const MARKUP_SIZES: ToolSizes;
|
|
7
|
+
/**
|
|
8
|
+
* Markup over the screens that have some, in canvas space, so it moves and scales with them; the
|
|
9
|
+
* screen being marked up takes the pointer. Everyone with the canvas open sees it as it is drawn.
|
|
10
|
+
*/
|
|
11
|
+
export declare function MarkupLayers({ layout, client, store, editor, }: {
|
|
12
|
+
layout: Layout;
|
|
13
|
+
client: DrawingClient;
|
|
14
|
+
store: ViewStore;
|
|
15
|
+
editor: DrawingEditor | null;
|
|
16
|
+
}): import("react").JSX.Element;
|
|
17
|
+
/**
|
|
18
|
+
* In place of the selection bar while a screen is marked up: its tools and the way out. `room` is the
|
|
19
|
+
* width the dock leaves it; short of it, the inks fold into one button.
|
|
20
|
+
*/
|
|
21
|
+
export declare function MarkupBar({ item, editor, onDone, room, }: {
|
|
22
|
+
item: CanvasItem;
|
|
23
|
+
editor: DrawingEditor;
|
|
24
|
+
onDone(): void;
|
|
25
|
+
room: number;
|
|
26
|
+
}): import("react").JSX.Element;
|
|
@@ -19,6 +19,8 @@ export interface ViewState {
|
|
|
19
19
|
frameEls: Record<string, HTMLIFrameElement | null>;
|
|
20
20
|
/** Inspect mode: screens take the pointer so the inspector can see it. */
|
|
21
21
|
inspecting: boolean;
|
|
22
|
+
/** The screen being marked up: its markup takes the pointer. */
|
|
23
|
+
markup: string | null;
|
|
22
24
|
}
|
|
23
25
|
export declare function createViewState(): Store<ViewState>;
|
|
24
26
|
export type ViewStore = ReturnType<typeof createViewState>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Theme } from '../../../../src/shared/types';
|
|
2
|
-
import { type RefObject } from 'react';
|
|
2
|
+
import { type ReactNode, type RefObject } from 'react';
|
|
3
3
|
import { type CameraStore } from './camera';
|
|
4
4
|
import { type FrameEvents } from './items';
|
|
5
5
|
import { type Layout } from './layout';
|
|
@@ -31,7 +31,9 @@ interface ViewportProps {
|
|
|
31
31
|
x: number;
|
|
32
32
|
y: number;
|
|
33
33
|
}): void;
|
|
34
|
+
/** Drawn over the items, in canvas space: screen markup. */
|
|
35
|
+
over?: ReactNode;
|
|
34
36
|
}
|
|
35
|
-
export declare function Viewport({ layout, theme, store, camera, events, capture, apiRef, onPlay, onSelect }: ViewportProps): import("react").JSX.Element;
|
|
37
|
+
export declare function Viewport({ layout, theme, store, camera, events, capture, apiRef, onPlay, onSelect, over, }: ViewportProps): import("react").JSX.Element;
|
|
36
38
|
export declare function isTyping(target: EventTarget | null): boolean;
|
|
37
39
|
export {};
|
|
@@ -84,5 +84,12 @@ export declare const ICON_PATHS: {
|
|
|
84
84
|
readonly keyboard: '<rect width="20" height="16" x="2" y="4" rx="2"/><path d="M6 8h.01"/><path d="M10 8h.01"/><path d="M14 8h.01"/><path d="M18 8h.01"/><path d="M8 12h.01"/><path d="M12 12h.01"/><path d="M16 12h.01"/><path d="M7 16h10"/>';
|
|
85
85
|
readonly folder: '<path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"/>';
|
|
86
86
|
readonly canvas: '<rect width="7" height="9" x="3" y="3" rx="1"/><rect width="7" height="5" x="14" y="3" rx="1"/><rect width="7" height="9" x="14" y="12" rx="1"/><rect width="7" height="5" x="3" y="16" rx="1"/>';
|
|
87
|
+
readonly eraser: '<path d="m7 21-4.3-4.3c-1-1-1-2.5 0-3.4l9.6-9.6c1-1 2.5-1 3.4 0l5.6 5.6c1 1 1 2.5 0 3.4L13 21"/><path d="M22 21H7"/><path d="m5 11 9 9"/>';
|
|
88
|
+
readonly square: '<rect width="18" height="18" x="3" y="3" rx="2"/>';
|
|
89
|
+
readonly 'arrow-up-right': '<path d="M13 5h6v6"/><path d="M19 5 5 19"/>';
|
|
90
|
+
readonly undo: '<path d="M9 14 4 9l5-5"/><path d="M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5 5.5 5.5 0 0 1-5.5 5.5H11"/>';
|
|
91
|
+
readonly redo: '<path d="m15 14 5-5-5-5"/><path d="M20 9H9.5A5.5 5.5 0 0 0 4 14.5 5.5 5.5 0 0 0 9.5 20H13"/>';
|
|
92
|
+
readonly minimize: '<path d="m14 10 7-7"/><path d="M20 10h-6V4"/><path d="m3 21 7-7"/><path d="M4 14h6v6"/>';
|
|
93
|
+
readonly lightbulb: '<path d="M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5"/><path d="M9 18h6"/><path d="M10 22h4"/>';
|
|
87
94
|
};
|
|
88
95
|
export type IconName = keyof typeof ICON_PATHS;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "foss-design",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.13",
|
|
4
4
|
"description": "Design canvases and design systems for coding agents: live, animated, clickable screens on an infinite canvas, served locally from a .design folder.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -72,6 +72,7 @@
|
|
|
72
72
|
"tailwind-merge": "^3.7.0",
|
|
73
73
|
"tailwindcss": "^4.3.3",
|
|
74
74
|
"vite": "^8.3.1",
|
|
75
|
+
"ws": "^8.22.0",
|
|
75
76
|
"zod": "^4.6.5"
|
|
76
77
|
},
|
|
77
78
|
"peerDependencies": {
|
|
@@ -97,7 +98,9 @@
|
|
|
97
98
|
"@types/node": "^24.13.6",
|
|
98
99
|
"@types/react": "^19.3.0",
|
|
99
100
|
"@types/react-dom": "^19.3.0",
|
|
101
|
+
"@types/ws": "^8.18.1",
|
|
100
102
|
"marked": "^18.0.14",
|
|
103
|
+
"perfect-freehand": "^1.2.3",
|
|
101
104
|
"sugar-high": "^2.5.0",
|
|
102
105
|
"tsup": "^8.5.1",
|
|
103
106
|
"typescript": "^7.0.2",
|