@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.
Files changed (43) hide show
  1. package/dist/board/codec.d.ts +2 -0
  2. package/dist/board/codec.js +18 -13
  3. package/dist/board/core/arrow-geometry.d.ts +23 -0
  4. package/dist/board/core/arrow-geometry.js +109 -0
  5. package/dist/board/core/connections.d.ts +83 -0
  6. package/dist/board/core/connections.js +399 -0
  7. package/dist/board/core/export-plan.d.ts +15 -4
  8. package/dist/board/core/export-plan.js +49 -16
  9. package/dist/board/core/shape-definition.js +1 -1
  10. package/dist/board/core/shape-types.d.ts +8 -2
  11. package/dist/board/core/shape-types.js +1 -1
  12. package/dist/board/core/tool-styles.d.ts +9 -3
  13. package/dist/board/core/tool-styles.js +10 -6
  14. package/dist/board/export/index.js +1 -0
  15. package/dist/board/export/scene.d.ts +3 -0
  16. package/dist/board/export/scene.js +18 -1
  17. package/dist/board/index.d.ts +8 -5
  18. package/dist/board/index.js +8 -5
  19. package/dist/board/render/connection-layer.d.ts +48 -0
  20. package/dist/board/render/connection-layer.js +223 -0
  21. package/dist/board/render/index.d.ts +2 -1
  22. package/dist/board/render/index.js +3 -2
  23. package/dist/board/render/renderers/arrow-card-renderer.js +35 -48
  24. package/dist/chunks/http.d.ts +53 -1
  25. package/dist/chunks/http.js +287 -1
  26. package/dist/chunks/websocket.d.ts +296 -57
  27. package/dist/http.d.ts +1 -1
  28. package/dist/index.d.ts +1 -1
  29. package/dist/index.js +17 -56
  30. package/dist/protocol/dist/board-connection.d.ts +310 -0
  31. package/dist/protocol/dist/board-connection.js +215 -0
  32. package/dist/protocol/dist/board-constants.d.ts +11 -1
  33. package/dist/protocol/dist/board-constants.js +15 -1
  34. package/dist/protocol/dist/board-document.d.ts +106 -60
  35. package/dist/protocol/dist/board-document.js +57 -17
  36. package/dist/protocol/dist/board.d.ts +1 -0
  37. package/dist/protocol/dist/board.js +3 -0
  38. package/dist/protocol/dist/index.d.ts +2 -1
  39. package/dist/protocol/dist/index.js +2 -1
  40. package/dist/protocol/dist/realtime/board-awareness.js +15 -15
  41. package/package.json +1 -1
  42. package/dist/board/core/bindings.d.ts +0 -45
  43. package/dist/board/core/bindings.js +0 -162
@@ -0,0 +1,223 @@
1
+ import { BOARD_FONT_STACK } from "../../protocol/dist/board-constants.js";
2
+ import { connectionArrowheads, resolveConnection } from "../core/connections.js";
3
+ import { pickBoardColor } from "../core/palette.js";
4
+ import { syncTextResolution, textResolutionForZoom } from "./text-resolution.js";
5
+ import { Container, Graphics, Text } from "pixi.js";
6
+ //#region src/board/render/connection-layer.ts
7
+ /**
8
+ * Connection drawing.
9
+ *
10
+ * Connections render into one shared layer beneath the cards rather than as a
11
+ * container per relation. A connection is a thin stroke with no texture and no
12
+ * interactive chrome of its own, so a per-connection container would add a
13
+ * transform, a render group and a draw call each for geometry that batches
14
+ * perfectly — on a densely connected board that is the difference between a few
15
+ * draw calls and a few thousand.
16
+ *
17
+ * Labels are the exception: text needs its own object to rasterise, so a `Text`
18
+ * is materialised only for connections that actually carry one and is pooled by
19
+ * connection id.
20
+ */
21
+ /** Arrowhead length relative to stroke width, and its floor in world units. */
22
+ const HEAD_SCALE = 5.5;
23
+ const HEAD_MIN = 11;
24
+ const HEAD_SPREAD = Math.PI / 6;
25
+ /** Dash pattern for `dashed` connections, in world units. */
26
+ const DASH_LENGTH = 10;
27
+ const DASH_GAP = 7;
28
+ /** Draw an open chevron arrowhead aimed along `angle`. */
29
+ function drawArrowhead(graphics, tip, angle, size, color, width, alpha) {
30
+ graphics.moveTo(tip.x - size * Math.cos(angle - HEAD_SPREAD), tip.y - size * Math.sin(angle - HEAD_SPREAD)).lineTo(tip.x, tip.y).lineTo(tip.x - size * Math.cos(angle + HEAD_SPREAD), tip.y - size * Math.sin(angle + HEAD_SPREAD)).stroke({
31
+ color,
32
+ width,
33
+ alpha,
34
+ cap: "round",
35
+ join: "round"
36
+ });
37
+ }
38
+ /** Trace a polyline as a dashed path, walking segment by segment. */
39
+ function traceDashed(graphics, path) {
40
+ let penDown = true;
41
+ let remaining = DASH_LENGTH;
42
+ let current = path[0];
43
+ if (!current) return;
44
+ graphics.moveTo(current.x, current.y);
45
+ for (let index = 1; index < path.length; index += 1) {
46
+ const next = path[index];
47
+ if (!next) continue;
48
+ let segmentRemaining = Math.hypot(next.x - current.x, next.y - current.y);
49
+ let from = current;
50
+ while (segmentRemaining > 1e-4) {
51
+ const step = Math.min(segmentRemaining, remaining);
52
+ const ratio = step / segmentRemaining;
53
+ const to = {
54
+ x: from.x + (next.x - from.x) * ratio,
55
+ y: from.y + (next.y - from.y) * ratio
56
+ };
57
+ if (penDown) graphics.lineTo(to.x, to.y);
58
+ else graphics.moveTo(to.x, to.y);
59
+ remaining -= step;
60
+ segmentRemaining -= step;
61
+ from = to;
62
+ if (remaining <= 1e-4) {
63
+ penDown = !penDown;
64
+ remaining = penDown ? DASH_LENGTH : DASH_GAP;
65
+ }
66
+ }
67
+ current = next;
68
+ }
69
+ }
70
+ function createConnectionLayer(options) {
71
+ let graphics = null;
72
+ let labelLayer = null;
73
+ const labels = /* @__PURE__ */ new Map();
74
+ let resolvedById = /* @__PURE__ */ new Map();
75
+ function ensureAttached() {
76
+ if (graphics && labelLayer) return {
77
+ graphics,
78
+ labelLayer
79
+ };
80
+ const nextGraphics = new Graphics({ label: "board-connections" });
81
+ const nextLabels = new Container({ label: "board-connection-labels" });
82
+ if (options.zIndex !== void 0) {
83
+ nextGraphics.zIndex = options.zIndex;
84
+ nextLabels.zIndex = options.zIndex;
85
+ }
86
+ options.parent.addChild(nextGraphics, nextLabels);
87
+ graphics = nextGraphics;
88
+ labelLayer = nextLabels;
89
+ return {
90
+ graphics: nextGraphics,
91
+ labelLayer: nextLabels
92
+ };
93
+ }
94
+ function releaseLabel(connectionId) {
95
+ const entry = labels.get(connectionId);
96
+ if (!entry) return;
97
+ labelLayer?.removeChild(entry.text);
98
+ entry.text.destroy();
99
+ labels.delete(connectionId);
100
+ }
101
+ function syncLabel(connection, resolved, input, color, host) {
102
+ const value = connection.label.trim();
103
+ if (!value) {
104
+ releaseLabel(connection.id);
105
+ return;
106
+ }
107
+ let entry = labels.get(connection.id);
108
+ if (!entry) {
109
+ const resolution = textResolutionForZoom(input.zoom);
110
+ const text = new Text({
111
+ text: value,
112
+ style: {
113
+ fill: color,
114
+ fontFamily: BOARD_FONT_STACK,
115
+ fontSize: 12,
116
+ fontWeight: "500"
117
+ },
118
+ resolution,
119
+ roundPixels: true
120
+ });
121
+ text.anchor.set(.5);
122
+ host.addChild(text);
123
+ entry = {
124
+ text,
125
+ resolution,
126
+ sig: ""
127
+ };
128
+ labels.set(connection.id, entry);
129
+ }
130
+ syncTextResolution(entry.text, entry, input.zoom);
131
+ const sig = `${value}|${color}`;
132
+ if (sig !== entry.sig) {
133
+ entry.sig = sig;
134
+ entry.text.text = value;
135
+ entry.text.style.fill = color;
136
+ }
137
+ entry.text.position.set(resolved.mid.x, resolved.mid.y);
138
+ }
139
+ function sync(input) {
140
+ if (input.connections.length === 0 && !graphics) {
141
+ resolvedById = /* @__PURE__ */ new Map();
142
+ return;
143
+ }
144
+ const host = ensureAttached();
145
+ host.graphics.clear();
146
+ const next = /* @__PURE__ */ new Map();
147
+ const selected = input.selectedIds ?? /* @__PURE__ */ new Set();
148
+ const skip = input.skipIds;
149
+ const minWidth = 1 / Math.max(input.zoom, 1e-4);
150
+ for (const connection of input.connections) {
151
+ const resolved = resolveConnection(connection, input.getFrame);
152
+ if (!resolved) continue;
153
+ next.set(connection.id, resolved);
154
+ if (skip?.has(connection.id)) {
155
+ releaseLabel(connection.id);
156
+ continue;
157
+ }
158
+ const isSelected = selected.has(connection.id);
159
+ const isHovered = input.hoveredId === connection.id;
160
+ const color = pickBoardColor(input.colors, connection.style.color, input.colorScheme);
161
+ const width = Math.max(connection.style.size + (isSelected ? 1 : 0), minWidth);
162
+ const alpha = isSelected || isHovered ? 1 : .9;
163
+ if (connection.style.line === "dashed") traceDashed(host.graphics, resolved.path);
164
+ else {
165
+ const first = resolved.path[0];
166
+ if (!first) continue;
167
+ host.graphics.moveTo(first.x, first.y);
168
+ for (let index = 1; index < resolved.path.length; index += 1) {
169
+ const point = resolved.path[index];
170
+ if (point) host.graphics.lineTo(point.x, point.y);
171
+ }
172
+ }
173
+ host.graphics.stroke({
174
+ color: color.stroke,
175
+ width,
176
+ alpha,
177
+ cap: "round",
178
+ join: "round"
179
+ });
180
+ const heads = connectionArrowheads(connection);
181
+ const headSize = Math.max(HEAD_MIN, connection.style.size * HEAD_SCALE);
182
+ if (heads.atTarget) {
183
+ const tip = resolved.path[resolved.path.length - 1];
184
+ const previous = resolved.path[resolved.path.length - 2];
185
+ if (tip && previous) drawArrowhead(host.graphics, tip, Math.atan2(tip.y - previous.y, tip.x - previous.x), headSize, color.stroke, width, alpha);
186
+ }
187
+ if (heads.atSource) {
188
+ const tip = resolved.path[0];
189
+ const next2 = resolved.path[1];
190
+ if (tip && next2) drawArrowhead(host.graphics, tip, Math.atan2(tip.y - next2.y, tip.x - next2.x), headSize, color.stroke, width, alpha);
191
+ }
192
+ syncLabel(connection, resolved, input, color.label, host.labelLayer);
193
+ }
194
+ for (const connectionId of [...labels.keys()]) if (!next.has(connectionId)) releaseLabel(connectionId);
195
+ resolvedById = next;
196
+ }
197
+ return {
198
+ sync,
199
+ resolved: (connectionId) => resolvedById.get(connectionId) ?? null,
200
+ get children() {
201
+ const list = [];
202
+ if (graphics) list.push(graphics);
203
+ if (labelLayer) list.push(labelLayer);
204
+ return list;
205
+ },
206
+ destroy: () => {
207
+ for (const entry of labels.values()) entry.text.destroy();
208
+ labels.clear();
209
+ graphics?.destroy();
210
+ labelLayer?.destroy({ children: true });
211
+ graphics = null;
212
+ labelLayer = null;
213
+ resolvedById = /* @__PURE__ */ new Map();
214
+ }
215
+ };
216
+ }
217
+ /** Frame lookup over a plain item list, for hosts without an index. */
218
+ function framesFromItems(items) {
219
+ const frames = new Map(items.map((item) => [item.id, item.frame]));
220
+ return (id) => frames.get(id);
221
+ }
222
+ //#endregion
223
+ export { createConnectionLayer, framesFromItems };
@@ -1,6 +1,7 @@
1
+ import { ConnectionLayer, ConnectionRenderInput, createConnectionLayer, framesFromItems } from "./connection-layer.js";
1
2
  import { BoardCardRenderer, BoardRenderContext, BoardRenderPalette, boardCardRenderersForTest, getBoardCardRenderer, registerBoardCardRenderer } from "./renderers/board-renderer-registry.js";
2
3
  import { defaultBoardPalette } from "./palette.js";
3
4
  import { ensureBoardTextMeasurement, installBoardTextMeasurement } from "./text-measurement.js";
4
5
  import { getBoardResolution, syncTextResolution, syncTextWrapWidth, textResolutionForZoom, textZoomBucket } from "./text-resolution.js";
5
6
  import { BoardThemeContext, BoardThemeRenderer, getBoardThemeRenderer, registerBoardThemeRenderer } from "./themes/board-theme-registry.js";
6
- export { BoardCardRenderer, BoardRenderContext, BoardRenderPalette, BoardThemeContext, BoardThemeRenderer, boardCardRenderersForTest, defaultBoardPalette, ensureBoardTextMeasurement, getBoardCardRenderer, getBoardResolution, getBoardThemeRenderer, installBoardTextMeasurement, registerBoardCardRenderer, registerBoardThemeRenderer, syncTextResolution, syncTextWrapWidth, textResolutionForZoom, textZoomBucket };
7
+ export { BoardCardRenderer, BoardRenderContext, BoardRenderPalette, BoardThemeContext, BoardThemeRenderer, ConnectionLayer, ConnectionRenderInput, boardCardRenderersForTest, createConnectionLayer, defaultBoardPalette, ensureBoardTextMeasurement, framesFromItems, getBoardCardRenderer, getBoardResolution, getBoardThemeRenderer, installBoardTextMeasurement, registerBoardCardRenderer, registerBoardThemeRenderer, syncTextResolution, syncTextWrapWidth, textResolutionForZoom, textZoomBucket };
@@ -1,6 +1,7 @@
1
+ import { getBoardResolution, syncTextResolution, syncTextWrapWidth, textResolutionForZoom, textZoomBucket } from "./text-resolution.js";
2
+ import { createConnectionLayer, framesFromItems } from "./connection-layer.js";
1
3
  import { defaultBoardPalette } from "./palette.js";
2
4
  import { ensureBoardTextMeasurement, installBoardTextMeasurement } from "./text-measurement.js";
3
- import { getBoardResolution, syncTextResolution, syncTextWrapWidth, textResolutionForZoom, textZoomBucket } from "./text-resolution.js";
4
5
  import { boardCardRenderersForTest, getBoardCardRenderer, registerBoardCardRenderer } from "./renderers/board-renderer-registry.js";
5
6
  import { getBoardThemeRenderer, registerBoardThemeRenderer } from "./themes/board-theme-registry.js";
6
- export { boardCardRenderersForTest, defaultBoardPalette, ensureBoardTextMeasurement, getBoardCardRenderer, getBoardResolution, getBoardThemeRenderer, installBoardTextMeasurement, registerBoardCardRenderer, registerBoardThemeRenderer, syncTextResolution, syncTextWrapWidth, textResolutionForZoom, textZoomBucket };
7
+ export { boardCardRenderersForTest, createConnectionLayer, defaultBoardPalette, ensureBoardTextMeasurement, framesFromItems, getBoardCardRenderer, getBoardResolution, getBoardThemeRenderer, installBoardTextMeasurement, registerBoardCardRenderer, registerBoardThemeRenderer, syncTextResolution, syncTextWrapWidth, textResolutionForZoom, textZoomBucket };
@@ -1,21 +1,11 @@
1
1
  import { BOARD_FONT_STACK } from "../../../protocol/dist/board-constants.js";
2
- import { resolveArrow, sampleQuadratic } from "../../core/bindings.js";
2
+ import { resolveArrow, sampleArrow } from "../../core/arrow-geometry.js";
3
3
  import { pickBoardColor } from "../../core/palette.js";
4
4
  import { syncTextResolution, textResolutionForZoom } from "../text-resolution.js";
5
5
  import { drawFarStroke } from "./far-plate.js";
6
6
  import { Container, Graphics, Text } from "pixi.js";
7
7
  //#region src/board/render/renderers/arrow-card-renderer.ts
8
8
  const partsByContainer = /* @__PURE__ */ new WeakMap();
9
- /**
10
- * Frame lookup for resolving bindings.
11
- *
12
- * Routed through the context's id index rather than scanning the document: an
13
- * arrow re-resolves its endpoints on every sync, and a per-arrow O(items) scan
14
- * would make a board with many arrows quadratic per frame.
15
- */
16
- function frameLookup(context) {
17
- return (id) => context.getItem(id)?.frame;
18
- }
19
9
  /** Open chevron arrowhead — clearly directional, not a tiny filled nub. */
20
10
  function drawArrowhead(graphics, tip, angle, size, color, strokeWidth) {
21
11
  const spread = Math.PI / 6;
@@ -44,8 +34,8 @@ function sync(container, item, context) {
44
34
  const hovered = context.hoveredId === item.id;
45
35
  const color = pickBoardColor(context.colors, item.color, context.colorScheme);
46
36
  syncTextResolution(parts.label, parts, context.zoom);
47
- const resolved = resolveArrow(item, frameLookup(context));
48
- const lineSig = resolved ? [
37
+ const resolved = resolveArrow(item);
38
+ const lineSig = [
49
39
  resolved.start.x,
50
40
  resolved.start.y,
51
41
  resolved.end.x,
@@ -58,39 +48,37 @@ function sync(container, item, context) {
58
48
  item.size,
59
49
  item.arrowStart,
60
50
  item.arrowEnd
61
- ].join("|") : `empty|${item.id}`;
51
+ ].join("|");
62
52
  if (lineSig !== parts.lineSig) {
63
53
  parts.lineSig = lineSig;
64
54
  parts.line.clear();
65
- if (resolved) {
66
- const strokeColor = color.stroke;
67
- const width = selected ? item.size + 1 : item.size;
68
- const samples = sampleQuadratic(resolved, 24);
69
- const head = samples[0];
70
- const tail = samples[samples.length - 1];
71
- if (head && tail) {
72
- parts.line.moveTo(head.x, head.y);
73
- for (let i = 1; i < samples.length; i += 1) {
74
- const point = samples[i];
75
- if (point) parts.line.lineTo(point.x, point.y);
76
- }
77
- parts.line.stroke({
78
- color: strokeColor,
79
- width,
80
- alpha: selected || hovered ? 1 : .92,
81
- cap: "round",
82
- join: "round"
83
- });
84
- const span = Math.hypot(tail.x - head.x, tail.y - head.y);
85
- const headSize = Math.min(Math.max(14, item.size * 5.5), Math.max(10, span * .28));
86
- if (item.arrowEnd) {
87
- const prev = samples[samples.length - 2];
88
- if (prev) drawArrowhead(parts.line, tail, Math.atan2(tail.y - prev.y, tail.x - prev.x), headSize, strokeColor, width);
89
- }
90
- if (item.arrowStart) {
91
- const next = samples[1];
92
- if (next) drawArrowhead(parts.line, head, Math.atan2(head.y - next.y, head.x - next.x), headSize, strokeColor, width);
93
- }
55
+ const strokeColor = color.stroke;
56
+ const width = selected ? item.size + 1 : item.size;
57
+ const samples = sampleArrow(resolved, 24);
58
+ const head = samples[0];
59
+ const tail = samples[samples.length - 1];
60
+ if (head && tail) {
61
+ parts.line.moveTo(head.x, head.y);
62
+ for (let i = 1; i < samples.length; i += 1) {
63
+ const point = samples[i];
64
+ if (point) parts.line.lineTo(point.x, point.y);
65
+ }
66
+ parts.line.stroke({
67
+ color: strokeColor,
68
+ width,
69
+ alpha: selected || hovered ? 1 : .92,
70
+ cap: "round",
71
+ join: "round"
72
+ });
73
+ const span = Math.hypot(tail.x - head.x, tail.y - head.y);
74
+ const headSize = Math.min(Math.max(14, item.size * 5.5), Math.max(10, span * .28));
75
+ if (item.arrowEnd) {
76
+ const prev = samples[samples.length - 2];
77
+ if (prev) drawArrowhead(parts.line, tail, Math.atan2(tail.y - prev.y, tail.x - prev.x), headSize, strokeColor, width);
78
+ }
79
+ if (item.arrowStart) {
80
+ const next = samples[1];
81
+ if (next) drawArrowhead(parts.line, head, Math.atan2(head.y - next.y, head.x - next.x), headSize, strokeColor, width);
94
82
  }
95
83
  }
96
84
  }
@@ -100,8 +88,8 @@ function sync(container, item, context) {
100
88
  parts.label.text = item.label;
101
89
  parts.label.style.fill = color.label;
102
90
  }
103
- parts.label.visible = Boolean(resolved && item.label.length > 0);
104
- if (resolved) parts.label.position.copyFrom(resolved.control);
91
+ parts.label.visible = item.label.length > 0;
92
+ parts.label.position.copyFrom(resolved.control);
105
93
  }
106
94
  const arrowCardRenderer = {
107
95
  id: "arrow-card",
@@ -139,10 +127,9 @@ const arrowCardRenderer = {
139
127
  },
140
128
  renderFar: (graphics, item, context) => {
141
129
  if (item.type !== "arrow") return;
142
- const resolved = resolveArrow(item, frameLookup(context));
143
- if (!resolved) return;
130
+ const resolved = resolveArrow(item);
144
131
  const color = pickBoardColor(context.colors, item.color, context.colorScheme);
145
- drawFarStroke(graphics, sampleQuadratic(resolved, 12), {
132
+ drawFarStroke(graphics, sampleArrow(resolved, 12), {
146
133
  color: color.stroke,
147
134
  width: item.size,
148
135
  alpha: .85
@@ -1,4 +1,4 @@
1
- import { $ as CreateSpacePromptInput, $n as SpaceFsTreeResponse, Ai as WorkContentKind, Ar as SpaceUsageResponse, At as Permission, Ba as ContentBlock, Bi as UiCommandError, Br as ChannelHealth, Bt as ReferenceAggregateGroupBy, Ca as MessageRecord, Ci as BoardAwarenessUpdate, Cn as SpaceCommerceBenefit, Ct as LabelResourceType, Da as SessionTurnRecord, Dn as SpaceCommerceOrder, Dt as ModelCatalogEntry, Er as SpaceTurnAuthorFilter, Et as MeResponse, Fn as SpaceCreateResponse, Fr as UserRulesResponse, Ft as PublicUserPageResponse, Gt as ReferenceQueryableType, Hi as UiCommandStatus, Hn as SpaceFsCreateUploadResponse, Ht as ReferenceDirection, Ia as SpaceCompletionResult, In as SpaceDefaultResponse, J as ClaimReferralResponse, Jn as SpaceFsMoveInput, Jt as ReferralDashboard, K as CheckpointDiffSummary, La as SpaceCompletionStreamEvent, Ln as SpaceEnvInput, Mn as SpaceConfigInput, Mr as TaskRunDetailResponse, Nn as SpaceConfigResponse, Nr as TaskRunRecord, Nt as PromptTemplateCatalogResponse, Oa as SpaceTurnsResponse, On as SpaceCommerceProduct, Ot as PatchResourceLabelsInput, Pa as CreateSpaceCompletionInput, Pn as SpaceConfigUpdateResponse, Pr as UserProfile, Pt as PublicReferral, Qi as BoardCapabilities, Qn as SpaceFsReadFilesResponse, R as Channel, Ra as Usage, Ri as UiCommand, Rn as SpaceFsCompleteUploadInput, Rr as UserSessionsResponse, Sa as SpacePublicEndpoints, Sn as SpaceCheckpointDetailResponse, Tr as SpaceSessionsResponse, Ut as ReferenceKind, V as CheckpointDiffFileResponse, Vi as UiCommandRecord, Vn as SpaceFsCreateUploadInput, Vt as ReferenceAggregateResponse, Wa as RequestSource, Wt as ReferenceQueryResponse, X as CreateInvitationResponse, Y as CreateInvitationInput, Yn as SpaceFsPreparingFile, Z as CreateSpaceInput, Zi as BoardBootstrap, _a as BoardTransaction, _r as SpacePresenceSnapshot, a as WebsocketClientOptions, an as SessionRecord, at as CursorPageInfo, br as SpaceRecord, ci as BoardTransactionAppliedEvent$1, cn as SessionTurnSignedUrlsResponse, d as BatchUserProfilesResponse, di as RealtimePatchOperation, dn as SessionTurnsPaginatedResponse, dt as GlobalSearchType, ea as BoardCreateInput, et as CreateSpacePromptResponse, fr as SpaceMember, ft as InvitationDetail, gn as SpaceAccessPolicy, gr as SpacePendingDiffSummary, hr as SpacePendingDiffFileResponse, ia as BoardInspectInput, ii as GenerationModelDeclaration, in as SessionMessagesResponse, it as CronJobUpdatePatch, kn as SpaceCommerceProductBenefitBinding, kt as PatchResourceLabelsResponse, l as AcceptInvitationResponse, ln as SessionTurnStreamSnapshotResponse, lr as SpaceInvitationListResponse, lt as GlobalSearchResponse, mr as SpaceModListItem, nn as SessionMessageResponse, oi as BoardAwarenessUpdatedEvent$1, on as SessionTurnIndexResponse, or as SpaceFsUploadResponse, pa as BoardPlaybackSnapshot, pn as SkillCatalogResponse, q as CheckpointRecord, qn as SpaceFsFileResponse, r as WebsocketClient, ri as GenerationContentBlock, rn as SessionMessagesPaginatedResponse, rt as CronJobRecord, s as WebsocketEventPayload, si as BoardPlaybackChangedEvent$1, sn as SessionTurnResponse, sr as SpaceFsWriteFileInput, tt as CreateSpaceSessionInput, ua as BoardPlaybackCommand, un as SessionTurnWindowResponse, va as BoardValidationResult, vi as SessionTurnPatchEvent, vt as LabelAssignmentRecord, wa as SessionForkRecord, wi as WorkArtifactDescriptor, wn as SpaceCommerceBuyerProfile, xr as SpaceRole, xt as LabelListItem, yr as SpacePublicProfile, yt as LabelItemsResponse, za as BillingPayload, zn as SpaceFsCompleteUploadResponse, zr as ChannelConfig } from "./websocket.js";
1
+ import { $ as CreateSpacePromptInput, $n as SpaceFsTreeResponse, Aa as SessionTurnRecord, Ai as WorkContentKind, Ar as SpaceUsageResponse, At as Permission, Ba as SpaceCompletionStreamEvent, Bi as UiCommandError, Br as ChannelHealth, Bt as ReferenceAggregateGroupBy, Ci as BoardAwarenessUpdate, Cn as SpaceCommerceBenefit, Ct as LabelResourceType, Da as SessionForkRecord, Dn as SpaceCommerceOrder, Dt as ModelCatalogEntry, Ea as MessageRecord, Er as SpaceTurnAuthorFilter, Et as MeResponse, Fn as SpaceCreateResponse, Fr as UserRulesResponse, Ft as PublicUserPageResponse, Gt as ReferenceQueryableType, Ha as BillingPayload, Hi as UiCommandStatus, Hn as SpaceFsCreateUploadResponse, Ht as ReferenceDirection, In as SpaceDefaultResponse, J as ClaimReferralResponse, Jn as SpaceFsMoveInput, Jt as ReferralDashboard, K as CheckpointDiffSummary, La as CreateSpaceCompletionInput, Ln as SpaceEnvInput, Mn as SpaceConfigInput, Mr as TaskRunDetailResponse, Nn as SpaceConfigResponse, Nr as TaskRunRecord, Nt as PromptTemplateCatalogResponse, On as SpaceCommerceProduct, Ot as PatchResourceLabelsInput, Pn as SpaceConfigUpdateResponse, Pr as UserProfile, Pt as PublicReferral, Qi as BoardCapabilities, Qn as SpaceFsReadFilesResponse, R as Channel, Ri as UiCommand, Rn as SpaceFsCompleteUploadInput, Rr as UserSessionsResponse, Sa as BoardConnectionRecord, Sn as SpaceCheckpointDetailResponse, Ta as SpacePublicEndpoints, Tr as SpaceSessionsResponse, Ua as ContentBlock, Ut as ReferenceKind, V as CheckpointDiffFileResponse, Va as Usage, Vi as UiCommandRecord, Vn as SpaceFsCreateUploadInput, Vt as ReferenceAggregateResponse, Wt as ReferenceQueryResponse, X as CreateInvitationResponse, Y as CreateInvitationInput, Yn as SpaceFsPreparingFile, Z as CreateSpaceInput, Zi as BoardBootstrap, _a as BoardTransaction, _r as SpacePresenceSnapshot, a as WebsocketClientOptions, an as SessionRecord, at as CursorPageInfo, ba as BoardConnection, br as SpaceRecord, ci as BoardTransactionAppliedEvent$1, cn as SessionTurnSignedUrlsResponse, d as BatchUserProfilesResponse, di as RealtimePatchOperation, dn as SessionTurnsPaginatedResponse, dt as GlobalSearchType, ea as BoardCreateInput, et as CreateSpacePromptResponse, fr as SpaceMember, ft as InvitationDetail, gn as SpaceAccessPolicy, gr as SpacePendingDiffSummary, hr as SpacePendingDiffFileResponse, ia as BoardInspectInput, ii as GenerationModelDeclaration, in as SessionMessagesResponse, it as CronJobUpdatePatch, ja as SpaceTurnsResponse, kn as SpaceCommerceProductBenefitBinding, kt as PatchResourceLabelsResponse, l as AcceptInvitationResponse, ln as SessionTurnStreamSnapshotResponse, lr as SpaceInvitationListResponse, lt as GlobalSearchResponse, mr as SpaceModListItem, nn as SessionMessageResponse, oi as BoardAwarenessUpdatedEvent$1, on as SessionTurnIndexResponse, or as SpaceFsUploadResponse, pa as BoardPlaybackSnapshot, pn as SkillCatalogResponse, q as CheckpointRecord, qa as RequestSource, qn as SpaceFsFileResponse, r as WebsocketClient, ri as GenerationContentBlock, rn as SessionMessagesPaginatedResponse, rt as CronJobRecord, s as WebsocketEventPayload, si as BoardPlaybackChangedEvent$1, sn as SessionTurnResponse, sr as SpaceFsWriteFileInput, tt as CreateSpaceSessionInput, ua as BoardPlaybackCommand, un as SessionTurnWindowResponse, va as BoardValidationResult, vi as SessionTurnPatchEvent, vt as LabelAssignmentRecord, wi as WorkArtifactDescriptor, wn as SpaceCommerceBuyerProfile, xa as BoardConnectionDirection, xr as SpaceRole, xt as LabelListItem, yr as SpacePublicProfile, yt as LabelItemsResponse, za as SpaceCompletionResult, zn as SpaceFsCompleteUploadResponse, zr as ChannelConfig } from "./websocket.js";
2
2
  import { n as CohubEnvironment } from "./environment.js";
3
3
  import { a as VoiceInputCreateOptions } from "./voice-input.js";
4
4
  //#region ../protocol/dist/model/status.d.ts
@@ -1445,6 +1445,58 @@ declare class BoardClient {
1445
1445
  apply(transaction: BoardTransactionInput): Promise<BoardBootstrap>;
1446
1446
  updateAwareness(seq: number, update: BoardAwarenessUpdate): Promise<void>;
1447
1447
  playback(command: BoardPlaybackCommand): Promise<BoardPlaybackSnapshot>;
1448
+ /**
1449
+ * Read the Board's relations.
1450
+ *
1451
+ * A dedicated read rather than a filter over `inspect()`: a caller that wants
1452
+ * the graph should not have to fetch every node's geometry to get it.
1453
+ */
1454
+ connections(customFetch?: Fetch): Promise<BoardConnectionRecord[]>;
1455
+ /**
1456
+ * Connections touching a node, in either direction.
1457
+ *
1458
+ * Filtered client-side from the Board's relation set, which is a single read and
1459
+ * bounded by the Board rather than by the node's degree.
1460
+ */
1461
+ connectionsForNode(nodeId: string, customFetch?: Fetch): Promise<BoardConnectionRecord[]>;
1462
+ /**
1463
+ * Connect two nodes.
1464
+ *
1465
+ * Wraps the transaction so the common case is one call: the caller supplies the
1466
+ * two nodes and, optionally, the relation. `baseVersion` still has to be the
1467
+ * version the caller last read, because a relation is only meaningful against
1468
+ * the node set it was authored on.
1469
+ */
1470
+ connect(input: {
1471
+ baseVersion: number;
1472
+ sourceNodeId: string;
1473
+ targetNodeId: string;
1474
+ id?: string;
1475
+ relation?: string;
1476
+ direction?: BoardConnectionDirection;
1477
+ label?: string;
1478
+ txId?: string;
1479
+ }): Promise<BoardBootstrap>;
1480
+ /** Remove a connection. The nodes it joined are untouched. */
1481
+ disconnect(input: {
1482
+ baseVersion: number;
1483
+ connectionId: string;
1484
+ txId?: string;
1485
+ }): Promise<BoardBootstrap>;
1486
+ /**
1487
+ * Delete a node together with every relation that names it.
1488
+ *
1489
+ * The server refuses to orphan a relation, so the cascade is explicit and lands
1490
+ * in one transaction: one undo step restores the node and its edges together.
1491
+ * `connections` is the relation set the caller already read, so this stays a
1492
+ * single round-trip.
1493
+ */
1494
+ deleteNodeWithConnections(input: {
1495
+ baseVersion: number;
1496
+ nodeId: string;
1497
+ connections: readonly BoardConnection[];
1498
+ txId?: string;
1499
+ }): Promise<BoardBootstrap>;
1448
1500
  play(command: Omit<Extract<BoardPlaybackCommand, {
1449
1501
  type: "play";
1450
1502
  }>, "shared"> & {