@neta-art/cohub 5.3.3 → 5.4.0
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/board/codec.d.ts +2 -0
- package/dist/board/codec.js +18 -13
- package/dist/board/core/arrow-geometry.d.ts +23 -0
- package/dist/board/core/arrow-geometry.js +109 -0
- package/dist/board/core/connections.d.ts +83 -0
- package/dist/board/core/connections.js +399 -0
- package/dist/board/core/export-plan.d.ts +15 -4
- package/dist/board/core/export-plan.js +49 -16
- package/dist/board/core/shape-definition.js +1 -1
- package/dist/board/core/shape-types.d.ts +8 -2
- package/dist/board/core/shape-types.js +1 -1
- package/dist/board/core/tool-styles.d.ts +9 -3
- package/dist/board/core/tool-styles.js +10 -6
- package/dist/board/export/index.js +1 -0
- package/dist/board/export/scene.d.ts +3 -0
- package/dist/board/export/scene.js +18 -1
- package/dist/board/index.d.ts +8 -5
- package/dist/board/index.js +8 -5
- package/dist/board/render/connection-layer.d.ts +48 -0
- package/dist/board/render/connection-layer.js +223 -0
- package/dist/board/render/index.d.ts +2 -1
- package/dist/board/render/index.js +3 -2
- package/dist/board/render/renderers/arrow-card-renderer.js +35 -48
- package/dist/chunks/http.d.ts +53 -1
- package/dist/chunks/http.js +287 -1
- package/dist/chunks/websocket.d.ts +296 -57
- package/dist/http.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +17 -56
- package/dist/protocol/dist/board-connection.d.ts +310 -0
- package/dist/protocol/dist/board-connection.js +215 -0
- package/dist/protocol/dist/board-constants.d.ts +11 -1
- package/dist/protocol/dist/board-constants.js +15 -1
- package/dist/protocol/dist/board-document.d.ts +106 -60
- package/dist/protocol/dist/board-document.js +57 -17
- package/dist/protocol/dist/board.d.ts +1 -0
- package/dist/protocol/dist/board.js +3 -0
- package/dist/protocol/dist/index.d.ts +2 -1
- package/dist/protocol/dist/index.js +2 -1
- package/dist/protocol/dist/realtime/board-awareness.js +15 -15
- package/package.json +1 -1
- package/dist/board/core/bindings.d.ts +0 -45
- package/dist/board/core/bindings.js +0 -162
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as parseAssistantMessageCommit, B as
|
|
1
|
+
import { A as parseAssistantMessageCommit, B as ReferencesApi, C as SpaceClient, D as buildSpacePath, E as buildSpaceInvitePath, F as BOARD_ARROW_STROKE_SIZE, G as ModelsApi, H as PublicAssetsApi, I as BOARD_BUILTIN_CAPABILITIES, J as ChannelsApi, K as GenerationsApi, L as BOARD_CONNECTION_STROKE_SIZE, M as createSessionPatchReducer, N as ensureRealtimeConnected, O as SessionGenerationStreamClient, P as BoardConnectionSchema, R as DEFAULT_BOARD_RENDER_LIMITS, S as BoardTransactionError, T as PublicInviteApi, U as SkillsApi, V as SearchApi, W as PromptsApi, _ as isUiSurfaceMethod, a as ReferralsApi, b as TasksApi, c as UiCommandsApi, d as UI_COMMAND_PAYLOAD_MAX_BYTES, f as UI_COMMAND_PENDING_TTL_SECONDS, g as isTerminalUiCommandStatus, h as UI_COMMAND_VERSION, i as WorksApi, j as SessionPatchReducer, k as createSessionGenerationStreamClient, l as UI_COMMAND_DEFAULT_TIMEOUT_MS, m as UI_COMMAND_TERMINAL_TTL_SECONDS, n as createHttpClient, o as UsersApi, p as UI_COMMAND_SETTLEMENT_GRACE_SECONDS, q as CronJobsApi, r as WorkCommerceApi, s as UserApi, t as CohubHttpClient, u as UI_COMMAND_MAX_TIMEOUT_MS, v as parseUiCommand, w as SpacesApi, x as BoardClient, y as parseUiCommandId, z as SessionAccessApi } from "./chunks/http.js";
|
|
2
2
|
import { _ as requestSourceToHeaders, a as sanitizeAccessToken, b as REALTIME_ROOM_EVENT_NAME_PATTERN, c as REQUEST_SOURCE_VIA_MAX_LENGTH, d as isRequestSourceEmpty, f as isRequestSourceUuid, g as readRequestSourceFromEnv, h as parseRequestSourceFromHeaders, i as matchesUnauthorizedErrorToken, l as hasRequestSourceIdentity, m as normalizeRequestSource, n as HttpTransport, o as COHUB_SOURCE_HEADER, p as mergeRequestSourceIntoMeta, r as joinApiUrl, s as COHUB_SOURCE_HEADER_NAMES, t as HttpError, u as isRequestSourceClientId, v as resolveRequestSourceChannel, y as REALTIME_DOMAINS } from "./chunks/transport.js";
|
|
3
3
|
import { a as resolveApiBaseUrl, c as resolveWebsocketUrl, i as normalizeWebsocketUrl, n as normalizeBaseUrl, o as resolveCohubEnvironment, r as normalizeVoiceInputWebsocketUrl, s as resolveVoiceInputWebsocketUrl, t as COHUB_ENVIRONMENTS } from "./chunks/environment.js";
|
|
4
4
|
import { a as extractBillingPayload, c as isFeatureNotEntitledError, i as FEATURE_NOT_ENTITLED_ERROR_CODE, l as isHttpErrorCode, n as createWebsocketClient, o as isBillingAccessBlockedCode, r as BILLING_ACCESS_BLOCKED_ERROR_CODE, s as isBillingAccessBlockedError, t as WebsocketClient } from "./chunks/websocket.js";
|
|
@@ -70,47 +70,6 @@ var BillingApi = class {
|
|
|
70
70
|
}
|
|
71
71
|
};
|
|
72
72
|
//#endregion
|
|
73
|
-
//#region ../protocol/dist/board-constants.js
|
|
74
|
-
const DEFAULT_BOARD_RENDER_LIMITS = {
|
|
75
|
-
particles: 2e4,
|
|
76
|
-
vertices: 5e5,
|
|
77
|
-
dynamicVertices: 15e4,
|
|
78
|
-
drawCalls: 400,
|
|
79
|
-
filterPasses: 24,
|
|
80
|
-
renderTexturePixels: 16777216,
|
|
81
|
-
textureBytes: 512 * 1024 * 1024,
|
|
82
|
-
bufferBytes: 256 * 1024 * 1024,
|
|
83
|
-
simulationSteps: 1e5
|
|
84
|
-
};
|
|
85
|
-
const BOARD_BUILTIN_CLIP_KINDS = [
|
|
86
|
-
"motion.keyframes",
|
|
87
|
-
"motion.path",
|
|
88
|
-
"draw.reveal",
|
|
89
|
-
"draw.handwrite",
|
|
90
|
-
"text.reveal",
|
|
91
|
-
"effects.particles",
|
|
92
|
-
"effects.trail",
|
|
93
|
-
"effects.impact",
|
|
94
|
-
"effects.flash",
|
|
95
|
-
"effects.color",
|
|
96
|
-
"camera.pan",
|
|
97
|
-
"camera.zoom",
|
|
98
|
-
"camera.shake"
|
|
99
|
-
];
|
|
100
|
-
const BOARD_BUILTIN_EFFECT_KINDS = ["effects.pulse", "effects.float"];
|
|
101
|
-
const BOARD_ARROW_STROKE_SIZE = 2.5;
|
|
102
|
-
const BOARD_BUILTIN_CAPABILITIES = [...BOARD_BUILTIN_CLIP_KINDS.map((id) => ({
|
|
103
|
-
kind: "clip",
|
|
104
|
-
id,
|
|
105
|
-
version: 1,
|
|
106
|
-
renderers: ["webgpu", "webgl"]
|
|
107
|
-
})), ...BOARD_BUILTIN_EFFECT_KINDS.map((id) => ({
|
|
108
|
-
kind: "effect",
|
|
109
|
-
id,
|
|
110
|
-
version: 1,
|
|
111
|
-
renderers: ["webgpu", "webgl"]
|
|
112
|
-
}))];
|
|
113
|
-
//#endregion
|
|
114
73
|
//#region ../protocol/dist/realtime/board-awareness.js
|
|
115
74
|
const idSchema$1 = z.string().min(1).max(160);
|
|
116
75
|
const finiteSchema$1 = z.number().finite();
|
|
@@ -126,23 +85,13 @@ const BoardAwarenessFrameSchema = z.object({
|
|
|
126
85
|
height: finiteSchema$1.positive(),
|
|
127
86
|
rotation: finiteSchema$1
|
|
128
87
|
});
|
|
129
|
-
const arrowEndpointSchema = z.union([z.object({
|
|
130
|
-
kind: z.literal("point"),
|
|
131
|
-
x: finiteSchema$1,
|
|
132
|
-
y: finiteSchema$1
|
|
133
|
-
}), z.object({
|
|
134
|
-
kind: z.literal("binding"),
|
|
135
|
-
target: idSchema$1,
|
|
136
|
-
nx: finiteSchema$1,
|
|
137
|
-
ny: finiteSchema$1,
|
|
138
|
-
precise: z.boolean()
|
|
139
|
-
})]);
|
|
140
88
|
const BoardAwarenessNodePreviewSchema = z.object({
|
|
141
89
|
nodeId: idSchema$1,
|
|
142
90
|
frame: BoardAwarenessFrameSchema,
|
|
91
|
+
/** Live endpoints of a free arrow being dragged. */
|
|
143
92
|
arrow: z.object({
|
|
144
|
-
start:
|
|
145
|
-
end:
|
|
93
|
+
start: BoardAwarenessPointSchema,
|
|
94
|
+
end: BoardAwarenessPointSchema,
|
|
146
95
|
bend: finiteSchema$1
|
|
147
96
|
}).optional()
|
|
148
97
|
});
|
|
@@ -191,6 +140,15 @@ const BoardAwarenessGestureSchema = z.discriminatedUnion("kind", [
|
|
|
191
140
|
color: z.string().min(1).max(64),
|
|
192
141
|
geo: z.string().min(1).max(40)
|
|
193
142
|
}),
|
|
143
|
+
z.object({
|
|
144
|
+
kind: z.literal("connection"),
|
|
145
|
+
id: idSchema$1,
|
|
146
|
+
sourceNodeId: idSchema$1,
|
|
147
|
+
targetNodeId: idSchema$1.nullable(),
|
|
148
|
+
current: BoardAwarenessPointSchema,
|
|
149
|
+
color: z.string().min(1).max(64),
|
|
150
|
+
size: finiteSchema$1.positive().max(256).default(BOARD_CONNECTION_STROKE_SIZE)
|
|
151
|
+
}),
|
|
194
152
|
z.object({
|
|
195
153
|
kind: z.literal("transform"),
|
|
196
154
|
id: idSchema$1,
|
|
@@ -198,7 +156,8 @@ const BoardAwarenessGestureSchema = z.discriminatedUnion("kind", [
|
|
|
198
156
|
"translate",
|
|
199
157
|
"resize",
|
|
200
158
|
"rotate",
|
|
201
|
-
"arrow"
|
|
159
|
+
"arrow",
|
|
160
|
+
"connection"
|
|
202
161
|
]),
|
|
203
162
|
nodes: z.array(BoardAwarenessNodePreviewSchema).max(64),
|
|
204
163
|
bounds: BoardAwarenessFrameSchema.nullable()
|
|
@@ -2572,6 +2531,7 @@ z.object({
|
|
|
2572
2531
|
title: z.string().min(1).max(255).optional(),
|
|
2573
2532
|
metadata: jsonObjectSchema.optional(),
|
|
2574
2533
|
nodes: z.array(BoardNodeInputSchema).max(5e4).optional(),
|
|
2534
|
+
connections: z.array(BoardConnectionSchema).max(5e4).optional(),
|
|
2575
2535
|
effects: z.array(BoardEffectSchema.omit({
|
|
2576
2536
|
boardId: true,
|
|
2577
2537
|
revision: true
|
|
@@ -2587,6 +2547,7 @@ z.object({
|
|
|
2587
2547
|
z.object({
|
|
2588
2548
|
include: z.array(z.enum([
|
|
2589
2549
|
"nodes",
|
|
2550
|
+
"connections",
|
|
2590
2551
|
"effects",
|
|
2591
2552
|
"sequences",
|
|
2592
2553
|
"clips",
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
//#region ../protocol/dist/board-connection.d.ts
|
|
3
|
+
/** Sides of a node frame a connection can attach to. */
|
|
4
|
+
declare const BOARD_CONNECTION_SIDES: readonly ["top", "right", "bottom", "left"];
|
|
5
|
+
type BoardConnectionSide = (typeof BOARD_CONNECTION_SIDES)[number];
|
|
6
|
+
/**
|
|
7
|
+
* Where a connection meets its node.
|
|
8
|
+
*
|
|
9
|
+
* - `auto` — the side is chosen from the live geometry of both endpoints, so the
|
|
10
|
+
* connection stays sensible through any move or resize. This is the default and
|
|
11
|
+
* what almost every connection should use.
|
|
12
|
+
* - `side` — the user pinned a side; `offset` (0..1) positions it along that edge.
|
|
13
|
+
* - `fixed` — the user pinned an exact normalized point on the frame.
|
|
14
|
+
*
|
|
15
|
+
* `auto` is a *declaration of intent*, not a computed value: the resolved side is
|
|
16
|
+
* never written back, so the connection keeps adapting instead of freezing the
|
|
17
|
+
* first layout it happened to have.
|
|
18
|
+
*/
|
|
19
|
+
declare const BoardConnectionAnchorSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
20
|
+
kind: z.ZodLiteral<"auto">;
|
|
21
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
22
|
+
kind: z.ZodLiteral<"side">;
|
|
23
|
+
side: z.ZodEnum<{
|
|
24
|
+
bottom: "bottom";
|
|
25
|
+
left: "left";
|
|
26
|
+
right: "right";
|
|
27
|
+
top: "top";
|
|
28
|
+
}>;
|
|
29
|
+
offset: z.ZodDefault<z.ZodNumber>;
|
|
30
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
31
|
+
kind: z.ZodLiteral<"fixed">;
|
|
32
|
+
nx: z.ZodNumber;
|
|
33
|
+
ny: z.ZodNumber;
|
|
34
|
+
}, z.core.$strip>]>;
|
|
35
|
+
type BoardConnectionAnchor = z.infer<typeof BoardConnectionAnchorSchema>;
|
|
36
|
+
declare const AUTO_BOARD_CONNECTION_ANCHOR: BoardConnectionAnchor;
|
|
37
|
+
declare const BoardConnectionEndpointSchema: z.ZodObject<{
|
|
38
|
+
nodeId: z.ZodString;
|
|
39
|
+
anchor: z.ZodDefault<z.ZodUnion<readonly [z.ZodObject<{
|
|
40
|
+
kind: z.ZodLiteral<"auto">;
|
|
41
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
42
|
+
kind: z.ZodLiteral<"side">;
|
|
43
|
+
side: z.ZodEnum<{
|
|
44
|
+
bottom: "bottom";
|
|
45
|
+
left: "left";
|
|
46
|
+
right: "right";
|
|
47
|
+
top: "top";
|
|
48
|
+
}>;
|
|
49
|
+
offset: z.ZodDefault<z.ZodNumber>;
|
|
50
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
51
|
+
kind: z.ZodLiteral<"fixed">;
|
|
52
|
+
nx: z.ZodNumber;
|
|
53
|
+
ny: z.ZodNumber;
|
|
54
|
+
}, z.core.$strip>]>>;
|
|
55
|
+
}, z.core.$strip>;
|
|
56
|
+
type BoardConnectionEndpoint = z.infer<typeof BoardConnectionEndpointSchema>;
|
|
57
|
+
/**
|
|
58
|
+
* Which ends carry an arrowhead.
|
|
59
|
+
*
|
|
60
|
+
* This is the *semantic* direction, not a style flag: `forward` means the
|
|
61
|
+
* relation reads source → target, `backward` means target → source, and `none`
|
|
62
|
+
* means the relation is symmetric. Renderers derive arrowheads from it, so the
|
|
63
|
+
* drawing can never disagree with the meaning.
|
|
64
|
+
*/
|
|
65
|
+
declare const BOARD_CONNECTION_DIRECTIONS: readonly ["none", "forward", "backward", "both"];
|
|
66
|
+
type BoardConnectionDirection = (typeof BOARD_CONNECTION_DIRECTIONS)[number];
|
|
67
|
+
/** How the line travels between its two resolved endpoints. */
|
|
68
|
+
declare const BOARD_CONNECTION_ROUTINGS: readonly ["straight", "curve", "orthogonal"];
|
|
69
|
+
type BoardConnectionRouting = (typeof BOARD_CONNECTION_ROUTINGS)[number];
|
|
70
|
+
declare const BOARD_CONNECTION_LINES: readonly ["solid", "dashed"];
|
|
71
|
+
type BoardConnectionLine = (typeof BOARD_CONNECTION_LINES)[number];
|
|
72
|
+
/**
|
|
73
|
+
* The default relation kind.
|
|
74
|
+
*
|
|
75
|
+
* "related" is deliberately unopinionated: drawing a line between two nodes
|
|
76
|
+
* states that they are connected, not *how*. A stronger claim (depends-on,
|
|
77
|
+
* blocks, ...) is something the user or an agent asserts explicitly.
|
|
78
|
+
*/
|
|
79
|
+
declare const DEFAULT_BOARD_RELATION: "related";
|
|
80
|
+
/**
|
|
81
|
+
* Relation kind — a free-form slug, not an enum.
|
|
82
|
+
*
|
|
83
|
+
* Boards are used for domains we do not control, so a closed vocabulary would
|
|
84
|
+
* force unrelated meanings into the wrong bucket. The format is constrained
|
|
85
|
+
* (lowercase, dash/dot separated) so relations stay comparable and queryable
|
|
86
|
+
* across clients instead of accumulating near-duplicate spellings.
|
|
87
|
+
*/
|
|
88
|
+
declare const BoardRelationSchema: z.ZodDefault<z.ZodString>;
|
|
89
|
+
/**
|
|
90
|
+
* Waypoints a user dragged the line through, in world space.
|
|
91
|
+
*
|
|
92
|
+
* Stored because they are *input*, not output: the resolved path is recomputed
|
|
93
|
+
* from them on every read, but the intent behind a hand-routed line cannot be
|
|
94
|
+
* recovered once discarded.
|
|
95
|
+
*/
|
|
96
|
+
declare const BoardConnectionWaypointSchema: z.ZodObject<{
|
|
97
|
+
x: z.ZodNumber;
|
|
98
|
+
y: z.ZodNumber;
|
|
99
|
+
}, z.core.$strip>;
|
|
100
|
+
declare const BoardConnectionRoutingSchema: z.ZodObject<{
|
|
101
|
+
kind: z.ZodDefault<z.ZodEnum<{
|
|
102
|
+
curve: "curve";
|
|
103
|
+
orthogonal: "orthogonal";
|
|
104
|
+
straight: "straight";
|
|
105
|
+
}>>;
|
|
106
|
+
bend: z.ZodDefault<z.ZodNumber>;
|
|
107
|
+
waypoints: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
108
|
+
x: z.ZodNumber;
|
|
109
|
+
y: z.ZodNumber;
|
|
110
|
+
}, z.core.$strip>>>;
|
|
111
|
+
}, z.core.$strip>;
|
|
112
|
+
type BoardConnectionRoutingConfig = z.infer<typeof BoardConnectionRoutingSchema>;
|
|
113
|
+
declare const BoardConnectionStyleSchema: z.ZodObject<{
|
|
114
|
+
color: z.ZodDefault<z.ZodString>;
|
|
115
|
+
size: z.ZodDefault<z.ZodNumber>;
|
|
116
|
+
line: z.ZodDefault<z.ZodEnum<{
|
|
117
|
+
dashed: "dashed";
|
|
118
|
+
solid: "solid";
|
|
119
|
+
}>>;
|
|
120
|
+
}, z.core.$strip>;
|
|
121
|
+
type BoardConnectionStyle = z.infer<typeof BoardConnectionStyleSchema>;
|
|
122
|
+
declare const DEFAULT_BOARD_CONNECTION_ROUTING: BoardConnectionRoutingConfig;
|
|
123
|
+
declare const DEFAULT_BOARD_CONNECTION_STYLE: BoardConnectionStyle;
|
|
124
|
+
declare const BoardConnectionSchema: z.ZodObject<{
|
|
125
|
+
id: z.ZodString;
|
|
126
|
+
source: z.ZodObject<{
|
|
127
|
+
nodeId: z.ZodString;
|
|
128
|
+
anchor: z.ZodDefault<z.ZodUnion<readonly [z.ZodObject<{
|
|
129
|
+
kind: z.ZodLiteral<"auto">;
|
|
130
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
131
|
+
kind: z.ZodLiteral<"side">;
|
|
132
|
+
side: z.ZodEnum<{
|
|
133
|
+
bottom: "bottom";
|
|
134
|
+
left: "left";
|
|
135
|
+
right: "right";
|
|
136
|
+
top: "top";
|
|
137
|
+
}>;
|
|
138
|
+
offset: z.ZodDefault<z.ZodNumber>;
|
|
139
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
140
|
+
kind: z.ZodLiteral<"fixed">;
|
|
141
|
+
nx: z.ZodNumber;
|
|
142
|
+
ny: z.ZodNumber;
|
|
143
|
+
}, z.core.$strip>]>>;
|
|
144
|
+
}, z.core.$strip>;
|
|
145
|
+
target: z.ZodObject<{
|
|
146
|
+
nodeId: z.ZodString;
|
|
147
|
+
anchor: z.ZodDefault<z.ZodUnion<readonly [z.ZodObject<{
|
|
148
|
+
kind: z.ZodLiteral<"auto">;
|
|
149
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
150
|
+
kind: z.ZodLiteral<"side">;
|
|
151
|
+
side: z.ZodEnum<{
|
|
152
|
+
bottom: "bottom";
|
|
153
|
+
left: "left";
|
|
154
|
+
right: "right";
|
|
155
|
+
top: "top";
|
|
156
|
+
}>;
|
|
157
|
+
offset: z.ZodDefault<z.ZodNumber>;
|
|
158
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
159
|
+
kind: z.ZodLiteral<"fixed">;
|
|
160
|
+
nx: z.ZodNumber;
|
|
161
|
+
ny: z.ZodNumber;
|
|
162
|
+
}, z.core.$strip>]>>;
|
|
163
|
+
}, z.core.$strip>;
|
|
164
|
+
relation: z.ZodDefault<z.ZodString>;
|
|
165
|
+
direction: z.ZodDefault<z.ZodEnum<{
|
|
166
|
+
backward: "backward";
|
|
167
|
+
both: "both";
|
|
168
|
+
forward: "forward";
|
|
169
|
+
none: "none";
|
|
170
|
+
}>>;
|
|
171
|
+
label: z.ZodDefault<z.ZodString>;
|
|
172
|
+
routing: z.ZodDefault<z.ZodObject<{
|
|
173
|
+
kind: z.ZodDefault<z.ZodEnum<{
|
|
174
|
+
curve: "curve";
|
|
175
|
+
orthogonal: "orthogonal";
|
|
176
|
+
straight: "straight";
|
|
177
|
+
}>>;
|
|
178
|
+
bend: z.ZodDefault<z.ZodNumber>;
|
|
179
|
+
waypoints: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
180
|
+
x: z.ZodNumber;
|
|
181
|
+
y: z.ZodNumber;
|
|
182
|
+
}, z.core.$strip>>>;
|
|
183
|
+
}, z.core.$strip>>;
|
|
184
|
+
style: z.ZodDefault<z.ZodObject<{
|
|
185
|
+
color: z.ZodDefault<z.ZodString>;
|
|
186
|
+
size: z.ZodDefault<z.ZodNumber>;
|
|
187
|
+
line: z.ZodDefault<z.ZodEnum<{
|
|
188
|
+
dashed: "dashed";
|
|
189
|
+
solid: "solid";
|
|
190
|
+
}>>;
|
|
191
|
+
}, z.core.$strip>>;
|
|
192
|
+
metadata: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
193
|
+
}, z.core.$strip>;
|
|
194
|
+
type BoardConnection = z.infer<typeof BoardConnectionSchema>;
|
|
195
|
+
/** Server-owned fields, mirroring how nodes carry a board id and revision. */
|
|
196
|
+
type BoardConnectionRecord = BoardConnection & {
|
|
197
|
+
boardId: string;
|
|
198
|
+
revision: number;
|
|
199
|
+
createdAt: string | null;
|
|
200
|
+
updatedAt: string | null;
|
|
201
|
+
};
|
|
202
|
+
/** The client-authored form of a connection (no server-owned fields). */
|
|
203
|
+
type BoardConnectionInput = BoardConnection;
|
|
204
|
+
/**
|
|
205
|
+
* A patch to an existing connection.
|
|
206
|
+
*
|
|
207
|
+
* `id` is excluded: a connection's identity never changes, and re-pointing both
|
|
208
|
+
* endpoints is an edit of the same relation, not a new one.
|
|
209
|
+
*/
|
|
210
|
+
declare const BoardConnectionPatchSchema: z.ZodObject<{
|
|
211
|
+
source: z.ZodOptional<z.ZodObject<{
|
|
212
|
+
nodeId: z.ZodString;
|
|
213
|
+
anchor: z.ZodDefault<z.ZodUnion<readonly [z.ZodObject<{
|
|
214
|
+
kind: z.ZodLiteral<"auto">;
|
|
215
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
216
|
+
kind: z.ZodLiteral<"side">;
|
|
217
|
+
side: z.ZodEnum<{
|
|
218
|
+
bottom: "bottom";
|
|
219
|
+
left: "left";
|
|
220
|
+
right: "right";
|
|
221
|
+
top: "top";
|
|
222
|
+
}>;
|
|
223
|
+
offset: z.ZodDefault<z.ZodNumber>;
|
|
224
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
225
|
+
kind: z.ZodLiteral<"fixed">;
|
|
226
|
+
nx: z.ZodNumber;
|
|
227
|
+
ny: z.ZodNumber;
|
|
228
|
+
}, z.core.$strip>]>>;
|
|
229
|
+
}, z.core.$strip>>;
|
|
230
|
+
target: z.ZodOptional<z.ZodObject<{
|
|
231
|
+
nodeId: z.ZodString;
|
|
232
|
+
anchor: z.ZodDefault<z.ZodUnion<readonly [z.ZodObject<{
|
|
233
|
+
kind: z.ZodLiteral<"auto">;
|
|
234
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
235
|
+
kind: z.ZodLiteral<"side">;
|
|
236
|
+
side: z.ZodEnum<{
|
|
237
|
+
bottom: "bottom";
|
|
238
|
+
left: "left";
|
|
239
|
+
right: "right";
|
|
240
|
+
top: "top";
|
|
241
|
+
}>;
|
|
242
|
+
offset: z.ZodDefault<z.ZodNumber>;
|
|
243
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
244
|
+
kind: z.ZodLiteral<"fixed">;
|
|
245
|
+
nx: z.ZodNumber;
|
|
246
|
+
ny: z.ZodNumber;
|
|
247
|
+
}, z.core.$strip>]>>;
|
|
248
|
+
}, z.core.$strip>>;
|
|
249
|
+
relation: z.ZodOptional<z.ZodDefault<z.ZodString>>;
|
|
250
|
+
direction: z.ZodOptional<z.ZodDefault<z.ZodEnum<{
|
|
251
|
+
backward: "backward";
|
|
252
|
+
both: "both";
|
|
253
|
+
forward: "forward";
|
|
254
|
+
none: "none";
|
|
255
|
+
}>>>;
|
|
256
|
+
label: z.ZodOptional<z.ZodDefault<z.ZodString>>;
|
|
257
|
+
routing: z.ZodOptional<z.ZodDefault<z.ZodObject<{
|
|
258
|
+
kind: z.ZodDefault<z.ZodEnum<{
|
|
259
|
+
curve: "curve";
|
|
260
|
+
orthogonal: "orthogonal";
|
|
261
|
+
straight: "straight";
|
|
262
|
+
}>>;
|
|
263
|
+
bend: z.ZodDefault<z.ZodNumber>;
|
|
264
|
+
waypoints: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
265
|
+
x: z.ZodNumber;
|
|
266
|
+
y: z.ZodNumber;
|
|
267
|
+
}, z.core.$strip>>>;
|
|
268
|
+
}, z.core.$strip>>>;
|
|
269
|
+
style: z.ZodOptional<z.ZodDefault<z.ZodObject<{
|
|
270
|
+
color: z.ZodDefault<z.ZodString>;
|
|
271
|
+
size: z.ZodDefault<z.ZodNumber>;
|
|
272
|
+
line: z.ZodDefault<z.ZodEnum<{
|
|
273
|
+
dashed: "dashed";
|
|
274
|
+
solid: "solid";
|
|
275
|
+
}>>;
|
|
276
|
+
}, z.core.$strip>>>;
|
|
277
|
+
metadata: z.ZodOptional<z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
278
|
+
}, z.core.$strip>;
|
|
279
|
+
type BoardConnectionPatch = z.infer<typeof BoardConnectionPatchSchema>;
|
|
280
|
+
/** Both node ids a connection touches, deduped for a self-loop. */
|
|
281
|
+
declare function connectionNodeIds(connection: BoardConnection): string[];
|
|
282
|
+
/** Whether a connection touches the given node. */
|
|
283
|
+
declare function connectionTouchesNode(connection: BoardConnection, nodeId: string): boolean;
|
|
284
|
+
/** The node at the far end of a connection from `nodeId`, or null. */
|
|
285
|
+
declare function connectionOtherNodeId(connection: BoardConnection, nodeId: string): string | null;
|
|
286
|
+
declare function normalizeBoardConnectionStyle(style: Partial<BoardConnectionStyle> | undefined): BoardConnectionStyle;
|
|
287
|
+
/**
|
|
288
|
+
* Build a connection with every default filled in.
|
|
289
|
+
*
|
|
290
|
+
* Callers only state what they mean (which nodes, and optionally the relation),
|
|
291
|
+
* so a connection created by the editor, an agent or the CLI is byte-identical
|
|
292
|
+
* for the same intent.
|
|
293
|
+
*/
|
|
294
|
+
declare function createBoardConnection(input: {
|
|
295
|
+
id: string;
|
|
296
|
+
sourceNodeId: string;
|
|
297
|
+
targetNodeId: string;
|
|
298
|
+
relation?: string;
|
|
299
|
+
direction?: BoardConnectionDirection;
|
|
300
|
+
label?: string;
|
|
301
|
+
sourceAnchor?: BoardConnectionAnchor;
|
|
302
|
+
targetAnchor?: BoardConnectionAnchor;
|
|
303
|
+
routing?: Partial<BoardConnectionRoutingConfig>;
|
|
304
|
+
style?: Partial<BoardConnectionStyle>;
|
|
305
|
+
metadata?: Record<string, unknown>;
|
|
306
|
+
}): BoardConnection;
|
|
307
|
+
/** Reverse a connection's endpoints, preserving the relation's reading order. */
|
|
308
|
+
declare function flipBoardConnection(connection: BoardConnection): BoardConnection;
|
|
309
|
+
//#endregion
|
|
310
|
+
export { AUTO_BOARD_CONNECTION_ANCHOR, BOARD_CONNECTION_DIRECTIONS, BOARD_CONNECTION_LINES, BOARD_CONNECTION_ROUTINGS, BOARD_CONNECTION_SIDES, BoardConnection, BoardConnectionAnchor, BoardConnectionAnchorSchema, BoardConnectionDirection, BoardConnectionEndpoint, BoardConnectionEndpointSchema, BoardConnectionInput, BoardConnectionLine, BoardConnectionPatch, BoardConnectionPatchSchema, BoardConnectionRecord, BoardConnectionRouting, BoardConnectionRoutingConfig, BoardConnectionRoutingSchema, BoardConnectionSchema, BoardConnectionSide, BoardConnectionStyle, BoardConnectionStyleSchema, BoardConnectionWaypointSchema, BoardRelationSchema, DEFAULT_BOARD_CONNECTION_ROUTING, DEFAULT_BOARD_CONNECTION_STYLE, DEFAULT_BOARD_RELATION, connectionNodeIds, connectionOtherNodeId, connectionTouchesNode, createBoardConnection, flipBoardConnection, normalizeBoardConnectionStyle };
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { BOARD_CONNECTION_STROKE_SIZE, clampBoardStrokeSize } from "./board-constants.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
//#region ../protocol/dist/board-connection.js
|
|
4
|
+
/**
|
|
5
|
+
* Board connections — the first-class relation between two Board nodes.
|
|
6
|
+
*
|
|
7
|
+
* A connection is *not* a shape. It has no frame of its own: its geometry is
|
|
8
|
+
* derived entirely from the two nodes it joins, which is exactly why it belongs
|
|
9
|
+
* in its own entity rather than in the item list. Storing it as a node would
|
|
10
|
+
* force a bounding box to be persisted and kept in sync with both endpoints —
|
|
11
|
+
* a second source of truth that is stale the moment either node moves.
|
|
12
|
+
*
|
|
13
|
+
* The stored form is therefore purely semantic: which nodes, in which direction,
|
|
14
|
+
* anchored where, routed how. Every world coordinate is resolved at read time
|
|
15
|
+
* from the live node frames (see the SDK's connection geometry), so a connection
|
|
16
|
+
* can never drift from the nodes it describes.
|
|
17
|
+
*
|
|
18
|
+
* Agents read this model directly: `source`, `target`, `relation` and `label`
|
|
19
|
+
* carry the meaning, while `routing` and `style` carry only presentation. A
|
|
20
|
+
* reader that wants the graph never has to interpret pixels.
|
|
21
|
+
*/
|
|
22
|
+
/** Sides of a node frame a connection can attach to. */
|
|
23
|
+
const BOARD_CONNECTION_SIDES = [
|
|
24
|
+
"top",
|
|
25
|
+
"right",
|
|
26
|
+
"bottom",
|
|
27
|
+
"left"
|
|
28
|
+
];
|
|
29
|
+
/**
|
|
30
|
+
* Where a connection meets its node.
|
|
31
|
+
*
|
|
32
|
+
* - `auto` — the side is chosen from the live geometry of both endpoints, so the
|
|
33
|
+
* connection stays sensible through any move or resize. This is the default and
|
|
34
|
+
* what almost every connection should use.
|
|
35
|
+
* - `side` — the user pinned a side; `offset` (0..1) positions it along that edge.
|
|
36
|
+
* - `fixed` — the user pinned an exact normalized point on the frame.
|
|
37
|
+
*
|
|
38
|
+
* `auto` is a *declaration of intent*, not a computed value: the resolved side is
|
|
39
|
+
* never written back, so the connection keeps adapting instead of freezing the
|
|
40
|
+
* first layout it happened to have.
|
|
41
|
+
*/
|
|
42
|
+
const BoardConnectionAnchorSchema = z.union([
|
|
43
|
+
z.object({ kind: z.literal("auto") }),
|
|
44
|
+
z.object({
|
|
45
|
+
kind: z.literal("side"),
|
|
46
|
+
side: z.enum(BOARD_CONNECTION_SIDES),
|
|
47
|
+
offset: z.number().finite().min(0).max(1).default(.5)
|
|
48
|
+
}),
|
|
49
|
+
z.object({
|
|
50
|
+
kind: z.literal("fixed"),
|
|
51
|
+
nx: z.number().finite().min(0).max(1),
|
|
52
|
+
ny: z.number().finite().min(0).max(1)
|
|
53
|
+
})
|
|
54
|
+
]);
|
|
55
|
+
const AUTO_BOARD_CONNECTION_ANCHOR = { kind: "auto" };
|
|
56
|
+
const BoardConnectionEndpointSchema = z.object({
|
|
57
|
+
nodeId: z.string().min(1).max(160),
|
|
58
|
+
anchor: BoardConnectionAnchorSchema.default(AUTO_BOARD_CONNECTION_ANCHOR)
|
|
59
|
+
});
|
|
60
|
+
/**
|
|
61
|
+
* Which ends carry an arrowhead.
|
|
62
|
+
*
|
|
63
|
+
* This is the *semantic* direction, not a style flag: `forward` means the
|
|
64
|
+
* relation reads source → target, `backward` means target → source, and `none`
|
|
65
|
+
* means the relation is symmetric. Renderers derive arrowheads from it, so the
|
|
66
|
+
* drawing can never disagree with the meaning.
|
|
67
|
+
*/
|
|
68
|
+
const BOARD_CONNECTION_DIRECTIONS = [
|
|
69
|
+
"none",
|
|
70
|
+
"forward",
|
|
71
|
+
"backward",
|
|
72
|
+
"both"
|
|
73
|
+
];
|
|
74
|
+
/** How the line travels between its two resolved endpoints. */
|
|
75
|
+
const BOARD_CONNECTION_ROUTINGS = [
|
|
76
|
+
"straight",
|
|
77
|
+
"curve",
|
|
78
|
+
"orthogonal"
|
|
79
|
+
];
|
|
80
|
+
const BOARD_CONNECTION_LINES = ["solid", "dashed"];
|
|
81
|
+
/**
|
|
82
|
+
* The default relation kind.
|
|
83
|
+
*
|
|
84
|
+
* "related" is deliberately unopinionated: drawing a line between two nodes
|
|
85
|
+
* states that they are connected, not *how*. A stronger claim (depends-on,
|
|
86
|
+
* blocks, ...) is something the user or an agent asserts explicitly.
|
|
87
|
+
*/
|
|
88
|
+
const DEFAULT_BOARD_RELATION = "related";
|
|
89
|
+
/**
|
|
90
|
+
* Relation kind — a free-form slug, not an enum.
|
|
91
|
+
*
|
|
92
|
+
* Boards are used for domains we do not control, so a closed vocabulary would
|
|
93
|
+
* force unrelated meanings into the wrong bucket. The format is constrained
|
|
94
|
+
* (lowercase, dash/dot separated) so relations stay comparable and queryable
|
|
95
|
+
* across clients instead of accumulating near-duplicate spellings.
|
|
96
|
+
*/
|
|
97
|
+
const BoardRelationSchema = z.string().min(1).max(64).regex(/^[a-z0-9]+(?:[.-][a-z0-9]+)*$/, "relation must be a lowercase slug").default(DEFAULT_BOARD_RELATION);
|
|
98
|
+
/**
|
|
99
|
+
* Waypoints a user dragged the line through, in world space.
|
|
100
|
+
*
|
|
101
|
+
* Stored because they are *input*, not output: the resolved path is recomputed
|
|
102
|
+
* from them on every read, but the intent behind a hand-routed line cannot be
|
|
103
|
+
* recovered once discarded.
|
|
104
|
+
*/
|
|
105
|
+
const BoardConnectionWaypointSchema = z.object({
|
|
106
|
+
x: z.number().finite(),
|
|
107
|
+
y: z.number().finite()
|
|
108
|
+
});
|
|
109
|
+
const BoardConnectionRoutingSchema = z.object({
|
|
110
|
+
kind: z.enum(BOARD_CONNECTION_ROUTINGS).default("curve"),
|
|
111
|
+
/** Curve bow as a fraction of endpoint distance (-0.85..0.85). */
|
|
112
|
+
bend: z.number().finite().min(-.85).max(.85).default(0),
|
|
113
|
+
waypoints: z.array(BoardConnectionWaypointSchema).max(64).default([])
|
|
114
|
+
});
|
|
115
|
+
const BoardConnectionStyleSchema = z.object({
|
|
116
|
+
/** Palette color id, resolved to a theme token at render time. */
|
|
117
|
+
color: z.string().min(1).max(40).default("brand"),
|
|
118
|
+
size: z.number().finite().positive().default(BOARD_CONNECTION_STROKE_SIZE),
|
|
119
|
+
line: z.enum(BOARD_CONNECTION_LINES).default("solid")
|
|
120
|
+
});
|
|
121
|
+
const DEFAULT_BOARD_CONNECTION_ROUTING = {
|
|
122
|
+
kind: "curve",
|
|
123
|
+
bend: 0,
|
|
124
|
+
waypoints: []
|
|
125
|
+
};
|
|
126
|
+
const DEFAULT_BOARD_CONNECTION_STYLE = {
|
|
127
|
+
color: "brand",
|
|
128
|
+
size: BOARD_CONNECTION_STROKE_SIZE,
|
|
129
|
+
line: "solid"
|
|
130
|
+
};
|
|
131
|
+
const BoardConnectionSchema = z.object({
|
|
132
|
+
id: z.string().min(1).max(160),
|
|
133
|
+
source: BoardConnectionEndpointSchema,
|
|
134
|
+
target: BoardConnectionEndpointSchema,
|
|
135
|
+
relation: BoardRelationSchema,
|
|
136
|
+
direction: z.enum(BOARD_CONNECTION_DIRECTIONS).default("forward"),
|
|
137
|
+
label: z.string().max(280).default(""),
|
|
138
|
+
routing: BoardConnectionRoutingSchema.default(DEFAULT_BOARD_CONNECTION_ROUTING),
|
|
139
|
+
style: BoardConnectionStyleSchema.default(DEFAULT_BOARD_CONNECTION_STYLE),
|
|
140
|
+
metadata: z.record(z.string(), z.unknown()).default({})
|
|
141
|
+
});
|
|
142
|
+
/**
|
|
143
|
+
* A patch to an existing connection.
|
|
144
|
+
*
|
|
145
|
+
* `id` is excluded: a connection's identity never changes, and re-pointing both
|
|
146
|
+
* endpoints is an edit of the same relation, not a new one.
|
|
147
|
+
*/
|
|
148
|
+
const BoardConnectionPatchSchema = BoardConnectionSchema.omit({ id: true }).partial();
|
|
149
|
+
/** Both node ids a connection touches, deduped for a self-loop. */
|
|
150
|
+
function connectionNodeIds(connection) {
|
|
151
|
+
return connection.source.nodeId === connection.target.nodeId ? [connection.source.nodeId] : [connection.source.nodeId, connection.target.nodeId];
|
|
152
|
+
}
|
|
153
|
+
/** Whether a connection touches the given node. */
|
|
154
|
+
function connectionTouchesNode(connection, nodeId) {
|
|
155
|
+
return connection.source.nodeId === nodeId || connection.target.nodeId === nodeId;
|
|
156
|
+
}
|
|
157
|
+
/** The node at the far end of a connection from `nodeId`, or null. */
|
|
158
|
+
function connectionOtherNodeId(connection, nodeId) {
|
|
159
|
+
if (connection.source.nodeId === nodeId) return connection.target.nodeId;
|
|
160
|
+
if (connection.target.nodeId === nodeId) return connection.source.nodeId;
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
function normalizeBoardConnectionStyle(style) {
|
|
164
|
+
return {
|
|
165
|
+
color: style?.color?.trim() || DEFAULT_BOARD_CONNECTION_STYLE.color,
|
|
166
|
+
size: clampBoardStrokeSize(typeof style?.size === "number" && Number.isFinite(style.size) ? style.size : DEFAULT_BOARD_CONNECTION_STYLE.size),
|
|
167
|
+
line: style?.line ?? DEFAULT_BOARD_CONNECTION_STYLE.line
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Build a connection with every default filled in.
|
|
172
|
+
*
|
|
173
|
+
* Callers only state what they mean (which nodes, and optionally the relation),
|
|
174
|
+
* so a connection created by the editor, an agent or the CLI is byte-identical
|
|
175
|
+
* for the same intent.
|
|
176
|
+
*/
|
|
177
|
+
function createBoardConnection(input) {
|
|
178
|
+
return {
|
|
179
|
+
id: input.id,
|
|
180
|
+
source: {
|
|
181
|
+
nodeId: input.sourceNodeId,
|
|
182
|
+
anchor: input.sourceAnchor ?? AUTO_BOARD_CONNECTION_ANCHOR
|
|
183
|
+
},
|
|
184
|
+
target: {
|
|
185
|
+
nodeId: input.targetNodeId,
|
|
186
|
+
anchor: input.targetAnchor ?? AUTO_BOARD_CONNECTION_ANCHOR
|
|
187
|
+
},
|
|
188
|
+
relation: input.relation ?? "related",
|
|
189
|
+
direction: input.direction ?? "forward",
|
|
190
|
+
label: input.label ?? "",
|
|
191
|
+
routing: {
|
|
192
|
+
...DEFAULT_BOARD_CONNECTION_ROUTING,
|
|
193
|
+
...input.routing
|
|
194
|
+
},
|
|
195
|
+
style: normalizeBoardConnectionStyle(input.style),
|
|
196
|
+
metadata: input.metadata ?? {}
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
/** Reverse a connection's endpoints, preserving the relation's reading order. */
|
|
200
|
+
function flipBoardConnection(connection) {
|
|
201
|
+
const direction = connection.direction === "forward" ? "backward" : connection.direction === "backward" ? "forward" : connection.direction;
|
|
202
|
+
return {
|
|
203
|
+
...connection,
|
|
204
|
+
source: connection.target,
|
|
205
|
+
target: connection.source,
|
|
206
|
+
direction,
|
|
207
|
+
routing: {
|
|
208
|
+
...connection.routing,
|
|
209
|
+
bend: -connection.routing.bend,
|
|
210
|
+
waypoints: [...connection.routing.waypoints].reverse()
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
//#endregion
|
|
215
|
+
export { AUTO_BOARD_CONNECTION_ANCHOR, BOARD_CONNECTION_DIRECTIONS, BOARD_CONNECTION_LINES, BOARD_CONNECTION_ROUTINGS, BOARD_CONNECTION_SIDES, BoardConnectionAnchorSchema, BoardConnectionEndpointSchema, BoardConnectionPatchSchema, BoardConnectionRoutingSchema, BoardConnectionSchema, BoardConnectionStyleSchema, BoardConnectionWaypointSchema, BoardRelationSchema, DEFAULT_BOARD_CONNECTION_ROUTING, DEFAULT_BOARD_CONNECTION_STYLE, DEFAULT_BOARD_RELATION, connectionNodeIds, connectionOtherNodeId, connectionTouchesNode, createBoardConnection, flipBoardConnection, normalizeBoardConnectionStyle };
|
|
@@ -19,5 +19,15 @@ type BoardCapability = {
|
|
|
19
19
|
fallbackId?: string;
|
|
20
20
|
schema?: Record<string, unknown>;
|
|
21
21
|
};
|
|
22
|
+
/**
|
|
23
|
+
* Stroke width bounds, shared by every stroked board entity.
|
|
24
|
+
*
|
|
25
|
+
* Defined here (not beside the editor) because the persisted schemas clamp with
|
|
26
|
+
* them: a single range is what keeps a width authored by the editor, an agent or
|
|
27
|
+
* the CLI from being silently re-clamped to something else on read.
|
|
28
|
+
*/
|
|
29
|
+
declare const BOARD_STROKE_MIN_SIZE = 1;
|
|
30
|
+
declare const BOARD_STROKE_MAX_SIZE = 64;
|
|
31
|
+
declare function clampBoardStrokeSize(size: number): number;
|
|
22
32
|
//#endregion
|
|
23
|
-
export { BoardCapability, BoardRenderCost };
|
|
33
|
+
export { BOARD_STROKE_MAX_SIZE, BOARD_STROKE_MIN_SIZE, BoardCapability, BoardRenderCost, clampBoardStrokeSize };
|