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.
@@ -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(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
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;