@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
|
@@ -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,
|
|
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
|
|
48
|
-
const lineSig =
|
|
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("|")
|
|
51
|
+
].join("|");
|
|
62
52
|
if (lineSig !== parts.lineSig) {
|
|
63
53
|
parts.lineSig = lineSig;
|
|
64
54
|
parts.line.clear();
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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 =
|
|
104
|
-
|
|
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
|
|
143
|
-
if (!resolved) return;
|
|
130
|
+
const resolved = resolveArrow(item);
|
|
144
131
|
const color = pickBoardColor(context.colors, item.color, context.colorScheme);
|
|
145
|
-
drawFarStroke(graphics,
|
|
132
|
+
drawFarStroke(graphics, sampleArrow(resolved, 12), {
|
|
146
133
|
color: color.stroke,
|
|
147
134
|
width: item.size,
|
|
148
135
|
alpha: .85
|
package/dist/chunks/http.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as CreateSpacePromptInput, $n as SpaceFsTreeResponse, Ai as WorkContentKind, Ar as SpaceUsageResponse, At as Permission, Ba as
|
|
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"> & {
|