@xenosystem/blocks 0.22.0 → 0.23.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/chunk-76V25B3E.js +435 -0
- package/dist/chunk-YSMDUREC.js +238 -0
- package/dist/chunk-YZ6J6A67.js +454 -0
- package/dist/corpus/index.d.ts +449 -0
- package/dist/corpus/index.js +991 -0
- package/dist/data/index.js +4 -2
- package/dist/edit/index.d.ts +162 -289
- package/dist/edit/index.js +30 -415
- package/dist/icons/index.js +4 -2
- package/dist/index.d.ts +1 -1
- package/dist/ops/index.js +4 -2
- package/dist/scale-XiQwxiFA.d.ts +130 -0
- package/dist/time/index.js +2 -1
- package/package.json +6 -2
- package/dist/chunk-6ZIWULSK.js +0 -629
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
// src/edit/curve/scale.ts
|
|
2
|
+
var DEFAULT_CURVE_ZOOM = { min: 5e-4, max: 64 };
|
|
3
|
+
var XenoCurveScale = class _XenoCurveScale {
|
|
4
|
+
pxPerTick;
|
|
5
|
+
scrollTick;
|
|
6
|
+
minValue;
|
|
7
|
+
maxValue;
|
|
8
|
+
widthPx;
|
|
9
|
+
heightPx;
|
|
10
|
+
gutterPx;
|
|
11
|
+
rulerPx;
|
|
12
|
+
constructor(state, geometry) {
|
|
13
|
+
this.widthPx = Math.max(1, finiteOr(geometry.widthPx, 1));
|
|
14
|
+
this.heightPx = Math.max(1, finiteOr(geometry.heightPx, 1));
|
|
15
|
+
this.gutterPx = Math.max(0, Math.min(finiteOr(geometry.gutterPx, 0), this.widthPx - 1));
|
|
16
|
+
this.rulerPx = Math.max(0, Math.min(finiteOr(geometry.rulerPx, 0), this.heightPx - 1));
|
|
17
|
+
this.pxPerTick = Math.max(Number.MIN_VALUE, finiteOr(state.pxPerTick, 1));
|
|
18
|
+
this.scrollTick = finiteOr(state.scrollTick, 0);
|
|
19
|
+
const min = finiteOr(state.minValue, 0);
|
|
20
|
+
const max = finiteOr(state.maxValue, 1);
|
|
21
|
+
if (max - min < 1e-12) {
|
|
22
|
+
const centre = (max + min) / 2;
|
|
23
|
+
this.minValue = centre - 0.5;
|
|
24
|
+
this.maxValue = centre + 0.5;
|
|
25
|
+
} else {
|
|
26
|
+
this.minValue = min;
|
|
27
|
+
this.maxValue = max;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** Width of the plot area. Always ≥ 1. */
|
|
31
|
+
get plotPx() {
|
|
32
|
+
return Math.max(1, this.widthPx - this.gutterPx);
|
|
33
|
+
}
|
|
34
|
+
/** Height of the plot area. Always ≥ 1. */
|
|
35
|
+
get plotHeightPx() {
|
|
36
|
+
return Math.max(1, this.heightPx - this.rulerPx);
|
|
37
|
+
}
|
|
38
|
+
/** Ticks visible. */
|
|
39
|
+
get spanTicks() {
|
|
40
|
+
return this.plotPx / this.pxPerTick;
|
|
41
|
+
}
|
|
42
|
+
/** Tick at the right edge. */
|
|
43
|
+
get endTick() {
|
|
44
|
+
return this.scrollTick + this.spanTicks;
|
|
45
|
+
}
|
|
46
|
+
/** Value units per pixel. */
|
|
47
|
+
get valuePerPx() {
|
|
48
|
+
return (this.maxValue - this.minValue) / this.plotHeightPx;
|
|
49
|
+
}
|
|
50
|
+
/** Container-relative x → tick. */
|
|
51
|
+
xToTick(x) {
|
|
52
|
+
return this.scrollTick + (finiteOr(x, 0) - this.gutterPx) / this.pxPerTick;
|
|
53
|
+
}
|
|
54
|
+
/** Tick → container-relative x. */
|
|
55
|
+
tickToX(tick) {
|
|
56
|
+
return this.gutterPx + (finiteOr(tick, 0) - this.scrollTick) * this.pxPerTick;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Container-relative y → value.
|
|
60
|
+
*
|
|
61
|
+
* **Inverted.** Screen y grows downward, value grows upward. Every sign error in a graph editor
|
|
62
|
+
* lives on this one line.
|
|
63
|
+
*/
|
|
64
|
+
yToValue(y) {
|
|
65
|
+
const local = finiteOr(y, 0) - this.rulerPx;
|
|
66
|
+
return this.maxValue - local / this.plotHeightPx * (this.maxValue - this.minValue);
|
|
67
|
+
}
|
|
68
|
+
/** Value → container-relative y. */
|
|
69
|
+
valueToY(value) {
|
|
70
|
+
const fraction = (this.maxValue - finiteOr(value, 0)) / (this.maxValue - this.minValue);
|
|
71
|
+
return this.rulerPx + fraction * this.plotHeightPx;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* A tick DURATION → a pixel width.
|
|
75
|
+
*
|
|
76
|
+
* Separate from {@link tickToX}, because rounding is not associative: mixing
|
|
77
|
+
* `durationToPx(b - a)` with `tickToX(b) - tickToX(a)` puts a curve's handle a pixel off its key
|
|
78
|
+
* at some zoom levels and not others.
|
|
79
|
+
*/
|
|
80
|
+
durationToPx(ticks) {
|
|
81
|
+
return finiteOr(ticks, 0) * this.pxPerTick;
|
|
82
|
+
}
|
|
83
|
+
/** A value DELTA → a pixel height. Sign-preserving and NOT inverted. */
|
|
84
|
+
valueDeltaToPx(delta) {
|
|
85
|
+
return finiteOr(delta, 0) / (this.maxValue - this.minValue) * this.plotHeightPx;
|
|
86
|
+
}
|
|
87
|
+
/** A pixel distance → a tick distance. For screen-space thresholds. */
|
|
88
|
+
pxToTicks(px) {
|
|
89
|
+
return finiteOr(px, 0) / this.pxPerTick;
|
|
90
|
+
}
|
|
91
|
+
/** A pixel distance → a value distance. For screen-space thresholds. */
|
|
92
|
+
pxToValue(px) {
|
|
93
|
+
return finiteOr(px, 0) * this.valuePerPx;
|
|
94
|
+
}
|
|
95
|
+
/** Is this point inside the plot area? */
|
|
96
|
+
isInPlot(x, y) {
|
|
97
|
+
return x >= this.gutterPx && x <= this.widthPx && y >= this.rulerPx && y <= this.heightPx;
|
|
98
|
+
}
|
|
99
|
+
/** A copy with new state. */
|
|
100
|
+
with(state) {
|
|
101
|
+
return new _XenoCurveScale(
|
|
102
|
+
{
|
|
103
|
+
pxPerTick: state.pxPerTick ?? this.pxPerTick,
|
|
104
|
+
scrollTick: state.scrollTick ?? this.scrollTick,
|
|
105
|
+
minValue: state.minValue ?? this.minValue,
|
|
106
|
+
maxValue: state.maxValue ?? this.maxValue
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
widthPx: this.widthPx,
|
|
110
|
+
heightPx: this.heightPx,
|
|
111
|
+
gutterPx: this.gutterPx,
|
|
112
|
+
rulerPx: this.rulerPx
|
|
113
|
+
}
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
/** A copy with new geometry. */
|
|
117
|
+
resized(geometry) {
|
|
118
|
+
return new _XenoCurveScale(
|
|
119
|
+
{
|
|
120
|
+
pxPerTick: this.pxPerTick,
|
|
121
|
+
scrollTick: this.scrollTick,
|
|
122
|
+
minValue: this.minValue,
|
|
123
|
+
maxValue: this.maxValue
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
widthPx: geometry.widthPx ?? this.widthPx,
|
|
127
|
+
heightPx: geometry.heightPx ?? this.heightPx,
|
|
128
|
+
gutterPx: geometry.gutterPx ?? this.gutterPx,
|
|
129
|
+
rulerPx: geometry.rulerPx ?? this.rulerPx
|
|
130
|
+
}
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
function finiteOr(value, fallback) {
|
|
135
|
+
return Number.isFinite(value) ? value : fallback;
|
|
136
|
+
}
|
|
137
|
+
function zoomTimeAboutCursor(scale, x, factor, bounds = DEFAULT_CURVE_ZOOM) {
|
|
138
|
+
const anchorTick = scale.xToTick(x);
|
|
139
|
+
const requested = scale.pxPerTick * (Number.isFinite(factor) && factor > 0 ? factor : 1);
|
|
140
|
+
const pxPerTick = Math.min(bounds.max, Math.max(bounds.min, requested));
|
|
141
|
+
return {
|
|
142
|
+
pxPerTick,
|
|
143
|
+
scrollTick: anchorTick - (x - scale.gutterPx) / pxPerTick,
|
|
144
|
+
minValue: scale.minValue,
|
|
145
|
+
maxValue: scale.maxValue
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
function zoomValueAboutCursor(scale, y, factor) {
|
|
149
|
+
const anchorValue = scale.yToValue(y);
|
|
150
|
+
const safeFactor = Number.isFinite(factor) && factor > 0 ? factor : 1;
|
|
151
|
+
const span = (scale.maxValue - scale.minValue) / safeFactor;
|
|
152
|
+
const fraction = (scale.maxValue - anchorValue) / (scale.maxValue - scale.minValue);
|
|
153
|
+
const maxValue = anchorValue + span * fraction;
|
|
154
|
+
return {
|
|
155
|
+
pxPerTick: scale.pxPerTick,
|
|
156
|
+
scrollTick: scale.scrollTick,
|
|
157
|
+
minValue: maxValue - span,
|
|
158
|
+
maxValue
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
function wheelZoomFactor(deltaY, sensitivity = 0.01) {
|
|
162
|
+
const raw = Number.isFinite(deltaY) ? deltaY : 0;
|
|
163
|
+
const clamped = Math.max(-10, Math.min(10, raw));
|
|
164
|
+
return Math.pow(2, -clamped * sensitivity * 10);
|
|
165
|
+
}
|
|
166
|
+
function fitValueRange(extent, marginFraction = 0.1) {
|
|
167
|
+
if (!extent || !Number.isFinite(extent.min) || !Number.isFinite(extent.max)) {
|
|
168
|
+
return { minValue: 0, maxValue: 1 };
|
|
169
|
+
}
|
|
170
|
+
const span = extent.max - extent.min;
|
|
171
|
+
const margin = Math.max(span * marginFraction, 0.5);
|
|
172
|
+
return { minValue: extent.min - margin, maxValue: extent.max + margin };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// src/edit/nodegraph/scale.ts
|
|
176
|
+
var DEFAULT_GRAPH_ZOOM = { min: 0.05, max: 4 };
|
|
177
|
+
var XenoGraphScale = class _XenoGraphScale {
|
|
178
|
+
zoom;
|
|
179
|
+
x;
|
|
180
|
+
y;
|
|
181
|
+
widthPx;
|
|
182
|
+
heightPx;
|
|
183
|
+
constructor(state, geometry) {
|
|
184
|
+
this.widthPx = Math.max(1, finiteOr2(geometry.widthPx, 1));
|
|
185
|
+
this.heightPx = Math.max(1, finiteOr2(geometry.heightPx, 1));
|
|
186
|
+
this.zoom = Math.max(Number.MIN_VALUE, finiteOr2(state.zoom, 1));
|
|
187
|
+
this.x = finiteOr2(state.x, 0);
|
|
188
|
+
this.y = finiteOr2(state.y, 0);
|
|
189
|
+
}
|
|
190
|
+
/** Graph-space width of the viewport. */
|
|
191
|
+
get graphWidth() {
|
|
192
|
+
return this.widthPx / this.zoom;
|
|
193
|
+
}
|
|
194
|
+
/** Graph-space height of the viewport. */
|
|
195
|
+
get graphHeight() {
|
|
196
|
+
return this.heightPx / this.zoom;
|
|
197
|
+
}
|
|
198
|
+
/** The visible rectangle in graph space. */
|
|
199
|
+
get viewport() {
|
|
200
|
+
return { x: this.x, y: this.y, width: this.graphWidth, height: this.graphHeight };
|
|
201
|
+
}
|
|
202
|
+
/** Container-relative screen point → graph point. */
|
|
203
|
+
screenToGraph(px, py) {
|
|
204
|
+
return { x: this.x + finiteOr2(px, 0) / this.zoom, y: this.y + finiteOr2(py, 0) / this.zoom };
|
|
205
|
+
}
|
|
206
|
+
/** Graph point → container-relative screen point. */
|
|
207
|
+
graphToScreen(gx, gy) {
|
|
208
|
+
return { x: (finiteOr2(gx, 0) - this.x) * this.zoom, y: (finiteOr2(gy, 0) - this.y) * this.zoom };
|
|
209
|
+
}
|
|
210
|
+
/** A graph-space distance → pixels. */
|
|
211
|
+
toPx(distance) {
|
|
212
|
+
return finiteOr2(distance, 0) * this.zoom;
|
|
213
|
+
}
|
|
214
|
+
/** A pixel distance → graph space. Used for screen-space hit thresholds. */
|
|
215
|
+
toGraph(px) {
|
|
216
|
+
return finiteOr2(px, 0) / this.zoom;
|
|
217
|
+
}
|
|
218
|
+
/** A copy with new state. */
|
|
219
|
+
with(state) {
|
|
220
|
+
return new _XenoGraphScale(
|
|
221
|
+
{ zoom: state.zoom ?? this.zoom, x: state.x ?? this.x, y: state.y ?? this.y },
|
|
222
|
+
{ widthPx: this.widthPx, heightPx: this.heightPx }
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
/** A copy with new geometry. */
|
|
226
|
+
resized(geometry) {
|
|
227
|
+
return new _XenoGraphScale(
|
|
228
|
+
{ zoom: this.zoom, x: this.x, y: this.y },
|
|
229
|
+
{ widthPx: geometry.widthPx ?? this.widthPx, heightPx: geometry.heightPx ?? this.heightPx }
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
function finiteOr2(value, fallback) {
|
|
234
|
+
return Number.isFinite(value) ? value : fallback;
|
|
235
|
+
}
|
|
236
|
+
function zoomAboutCursor(scale, px, py, factor, bounds = DEFAULT_GRAPH_ZOOM) {
|
|
237
|
+
const anchor = scale.screenToGraph(px, py);
|
|
238
|
+
const requested = scale.zoom * (Number.isFinite(factor) && factor > 0 ? factor : 1);
|
|
239
|
+
const zoom = Math.min(bounds.max, Math.max(bounds.min, requested));
|
|
240
|
+
return { zoom, x: anchor.x - px / zoom, y: anchor.y - py / zoom };
|
|
241
|
+
}
|
|
242
|
+
function fitToBounds(bounds, widthPx, heightPx, zoomBounds = DEFAULT_GRAPH_ZOOM, paddingPx = 40) {
|
|
243
|
+
if (!bounds || bounds.width <= 0 || bounds.height <= 0) return { zoom: 1, x: 0, y: 0 };
|
|
244
|
+
const usableW = Math.max(1, widthPx - paddingPx * 2);
|
|
245
|
+
const usableH = Math.max(1, heightPx - paddingPx * 2);
|
|
246
|
+
const zoom = Math.min(
|
|
247
|
+
zoomBounds.max,
|
|
248
|
+
Math.max(zoomBounds.min, Math.min(usableW / bounds.width, usableH / bounds.height))
|
|
249
|
+
);
|
|
250
|
+
const x = bounds.x + bounds.width / 2 - widthPx / zoom / 2;
|
|
251
|
+
const y = bounds.y + bounds.height / 2 - heightPx / zoom / 2;
|
|
252
|
+
return { zoom, x, y };
|
|
253
|
+
}
|
|
254
|
+
function graphBounds(nodes) {
|
|
255
|
+
if (nodes.length === 0) return null;
|
|
256
|
+
let minX = Number.POSITIVE_INFINITY;
|
|
257
|
+
let minY = Number.POSITIVE_INFINITY;
|
|
258
|
+
let maxX = Number.NEGATIVE_INFINITY;
|
|
259
|
+
let maxY = Number.NEGATIVE_INFINITY;
|
|
260
|
+
for (const node of nodes) {
|
|
261
|
+
if (!Number.isFinite(node.x) || !Number.isFinite(node.y)) continue;
|
|
262
|
+
minX = Math.min(minX, node.x);
|
|
263
|
+
minY = Math.min(minY, node.y);
|
|
264
|
+
maxX = Math.max(maxX, node.x + Math.max(1, node.width));
|
|
265
|
+
maxY = Math.max(maxY, node.y + Math.max(1, node.height));
|
|
266
|
+
}
|
|
267
|
+
if (!Number.isFinite(minX)) return null;
|
|
268
|
+
return { x: minX, y: minY, width: Math.max(1, maxX - minX), height: Math.max(1, maxY - minY) };
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// src/edit/nodegraph/types.ts
|
|
272
|
+
function portRefId(ref) {
|
|
273
|
+
return `${ref.nodeId}${ref.portId}`;
|
|
274
|
+
}
|
|
275
|
+
function edgeKey(edge) {
|
|
276
|
+
return `${edge.fromNodeId}${edge.fromPortId}${edge.toNodeId}${edge.toPortId}`;
|
|
277
|
+
}
|
|
278
|
+
function findPort(node, portId) {
|
|
279
|
+
return node.inputs.find((p) => p.id === portId) ?? node.outputs.find((p) => p.id === portId) ?? null;
|
|
280
|
+
}
|
|
281
|
+
function portHasRoom(port, existingCount) {
|
|
282
|
+
if (port.multiple !== void 0) return port.multiple || existingCount === 0;
|
|
283
|
+
return port.direction === "out" ? true : existingCount === 0;
|
|
284
|
+
}
|
|
285
|
+
function nodeBounds(node) {
|
|
286
|
+
return { x: node.x, y: node.y, width: Math.max(1, node.width), height: Math.max(1, node.height) };
|
|
287
|
+
}
|
|
288
|
+
function boxesIntersect(a, b) {
|
|
289
|
+
return a.x < b.x + b.width && b.x < a.x + a.width && a.y < b.y + b.height && b.y < a.y + a.height;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// src/edit/nodegraph/spatial.ts
|
|
293
|
+
var XenoNodeIndex = class {
|
|
294
|
+
cellSize;
|
|
295
|
+
cells = /* @__PURE__ */ new Map();
|
|
296
|
+
all;
|
|
297
|
+
constructor(nodes) {
|
|
298
|
+
this.all = nodes;
|
|
299
|
+
this.cellSize = chooseCellSize(nodes);
|
|
300
|
+
for (const node of nodes) {
|
|
301
|
+
const x0 = Math.floor(node.x / this.cellSize);
|
|
302
|
+
const y0 = Math.floor(node.y / this.cellSize);
|
|
303
|
+
const x1 = Math.floor((node.x + Math.max(1, node.width)) / this.cellSize);
|
|
304
|
+
const y1 = Math.floor((node.y + Math.max(1, node.height)) / this.cellSize);
|
|
305
|
+
for (let cx = x0; cx <= x1; cx += 1) {
|
|
306
|
+
for (let cy = y0; cy <= y1; cy += 1) {
|
|
307
|
+
const key = `${cx},${cy}`;
|
|
308
|
+
const bucket = this.cells.get(key);
|
|
309
|
+
if (bucket) bucket.push(node);
|
|
310
|
+
else this.cells.set(key, [node]);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
/** Bucket size, for tests. */
|
|
316
|
+
get bucketSize() {
|
|
317
|
+
return this.cellSize;
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Nodes intersecting a box.
|
|
321
|
+
*
|
|
322
|
+
* @param box - The query rectangle in graph space.
|
|
323
|
+
* @returns Matching nodes, each at most once.
|
|
324
|
+
*/
|
|
325
|
+
query(box) {
|
|
326
|
+
const x0 = Math.floor(box.x / this.cellSize);
|
|
327
|
+
const y0 = Math.floor(box.y / this.cellSize);
|
|
328
|
+
const x1 = Math.floor((box.x + box.width) / this.cellSize);
|
|
329
|
+
const y1 = Math.floor((box.y + box.height) / this.cellSize);
|
|
330
|
+
const seen = /* @__PURE__ */ new Set();
|
|
331
|
+
const out = [];
|
|
332
|
+
for (let cx = x0; cx <= x1; cx += 1) {
|
|
333
|
+
for (let cy = y0; cy <= y1; cy += 1) {
|
|
334
|
+
for (const node of this.cells.get(`${cx},${cy}`) ?? []) {
|
|
335
|
+
if (seen.has(node.id)) continue;
|
|
336
|
+
seen.add(node.id);
|
|
337
|
+
if (boxesIntersect(node, { ...box, width: Math.max(0, box.width), height: Math.max(0, box.height) })) {
|
|
338
|
+
out.push(node);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return out;
|
|
344
|
+
}
|
|
345
|
+
/** The topmost node containing a point, or `null`. */
|
|
346
|
+
hit(x, y) {
|
|
347
|
+
const candidates = this.query({ x, y, width: 1e-3, height: 1e-3 });
|
|
348
|
+
if (candidates.length === 0) return null;
|
|
349
|
+
let best = null;
|
|
350
|
+
for (const node of candidates) {
|
|
351
|
+
if (x >= node.x && x <= node.x + node.width && y >= node.y && y <= node.y + node.height) {
|
|
352
|
+
if (!best || this.all.indexOf(node) > this.all.indexOf(best)) best = node;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return best;
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
function chooseCellSize(nodes) {
|
|
359
|
+
if (nodes.length === 0) return 256;
|
|
360
|
+
const sizes = [];
|
|
361
|
+
for (const node of nodes) {
|
|
362
|
+
const size = Math.max(node.width || 1, node.height || 1);
|
|
363
|
+
if (Number.isFinite(size) && size > 0) sizes.push(size);
|
|
364
|
+
}
|
|
365
|
+
if (sizes.length === 0) return 256;
|
|
366
|
+
sizes.sort((a, b) => a - b);
|
|
367
|
+
const median = sizes[Math.floor(sizes.length / 2)];
|
|
368
|
+
return Math.max(32, Math.min(4096, median * 2));
|
|
369
|
+
}
|
|
370
|
+
function edgeBounds(from, to, slack = 64) {
|
|
371
|
+
const x = Math.min(from.x, to.x) - slack;
|
|
372
|
+
const y = Math.min(from.y, to.y) - slack;
|
|
373
|
+
return {
|
|
374
|
+
x,
|
|
375
|
+
y,
|
|
376
|
+
width: Math.abs(to.x - from.x) + slack * 2,
|
|
377
|
+
height: Math.abs(to.y - from.y) + slack * 2
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
function visibleEdges(edges, portPosition, viewport) {
|
|
381
|
+
const out = [];
|
|
382
|
+
for (const edge of edges) {
|
|
383
|
+
const from = portPosition(edge.fromNodeId, edge.fromPortId);
|
|
384
|
+
const to = portPosition(edge.toNodeId, edge.toPortId);
|
|
385
|
+
if (!from || !to) continue;
|
|
386
|
+
if (!boxesIntersect(edgeBounds(from, to), viewport)) continue;
|
|
387
|
+
out.push({ edge, from, to });
|
|
388
|
+
}
|
|
389
|
+
return out;
|
|
390
|
+
}
|
|
391
|
+
function edgePath(from, to) {
|
|
392
|
+
const dx = Math.abs(to.x - from.x);
|
|
393
|
+
const reach = Math.max(40, Math.min(dx * 0.6, 240));
|
|
394
|
+
return `M${from.x},${from.y} C${from.x + reach},${from.y} ${to.x - reach},${to.y} ${to.x},${to.y}`;
|
|
395
|
+
}
|
|
396
|
+
function distanceToEdge(point, from, to, samples = 16) {
|
|
397
|
+
const dx = Math.abs(to.x - from.x);
|
|
398
|
+
const reach = Math.max(40, Math.min(dx * 0.6, 240));
|
|
399
|
+
const c1 = { x: from.x + reach, y: from.y };
|
|
400
|
+
const c2 = { x: to.x - reach, y: to.y };
|
|
401
|
+
let best = Number.POSITIVE_INFINITY;
|
|
402
|
+
for (let i = 0; i <= samples; i += 1) {
|
|
403
|
+
const t = i / samples;
|
|
404
|
+
const mt = 1 - t;
|
|
405
|
+
const x = mt * mt * mt * from.x + 3 * mt * mt * t * c1.x + 3 * mt * t * t * c2.x + t * t * t * to.x;
|
|
406
|
+
const y = mt * mt * mt * from.y + 3 * mt * mt * t * c1.y + 3 * mt * t * t * c2.y + t * t * t * to.y;
|
|
407
|
+
best = Math.min(best, Math.hypot(x - point.x, y - point.y));
|
|
408
|
+
}
|
|
409
|
+
return best;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
export {
|
|
413
|
+
DEFAULT_CURVE_ZOOM,
|
|
414
|
+
XenoCurveScale,
|
|
415
|
+
zoomTimeAboutCursor,
|
|
416
|
+
zoomValueAboutCursor,
|
|
417
|
+
wheelZoomFactor,
|
|
418
|
+
fitValueRange,
|
|
419
|
+
DEFAULT_GRAPH_ZOOM,
|
|
420
|
+
XenoGraphScale,
|
|
421
|
+
zoomAboutCursor,
|
|
422
|
+
fitToBounds,
|
|
423
|
+
graphBounds,
|
|
424
|
+
portRefId,
|
|
425
|
+
edgeKey,
|
|
426
|
+
findPort,
|
|
427
|
+
portHasRoom,
|
|
428
|
+
nodeBounds,
|
|
429
|
+
boxesIntersect,
|
|
430
|
+
XenoNodeIndex,
|
|
431
|
+
edgeBounds,
|
|
432
|
+
visibleEdges,
|
|
433
|
+
edgePath,
|
|
434
|
+
distanceToEdge
|
|
435
|
+
};
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
// src/icons/glyphs.tsx
|
|
2
|
+
import Copy from "@xenosystem/elements/elements/copy";
|
|
3
|
+
import Download from "@xenosystem/elements/elements/download";
|
|
4
|
+
import Plus from "@xenosystem/elements/elements/plus";
|
|
5
|
+
import Minus from "@xenosystem/elements/elements/minus";
|
|
6
|
+
import Refresh from "@xenosystem/elements/elements/refresh";
|
|
7
|
+
import X from "@xenosystem/elements/elements/x";
|
|
8
|
+
|
|
9
|
+
// src/icons/elements.tsx
|
|
10
|
+
import { XenoElement } from "@xenosystem/elements-react";
|
|
11
|
+
import { jsx } from "react/jsx-runtime";
|
|
12
|
+
var GLYPH_STROKE_WIDTH = 1.5;
|
|
13
|
+
function fromElement(decl, defaultSize = 11, state) {
|
|
14
|
+
const Glyph = ({ size = defaultSize, className }) => /* @__PURE__ */ jsx(XenoElement, { decl, state, size, strokeWidth: GLYPH_STROKE_WIDTH, className, "aria-hidden": "true", role: void 0, "aria-label": void 0 });
|
|
15
|
+
Glyph.displayName = `Element(${decl.id})`;
|
|
16
|
+
return Glyph;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// src/icons/glyphs.tsx
|
|
20
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
21
|
+
function Svg({ children, size = 11 }) {
|
|
22
|
+
return /* @__PURE__ */ jsx2("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children });
|
|
23
|
+
}
|
|
24
|
+
var RefreshGlyph = fromElement(Refresh);
|
|
25
|
+
var XGlyph = fromElement(X);
|
|
26
|
+
var PlusGlyph = fromElement(Plus);
|
|
27
|
+
var MinusGlyph = fromElement(Minus);
|
|
28
|
+
function ChevronGlyph({ direction = "right", size } = {}) {
|
|
29
|
+
const d = direction === "left" ? "m15 18-6-6 6-6" : direction === "up" ? "m18 15-6-6-6 6" : direction === "down" ? "m6 9 6 6 6-6" : "m9 18 6-6-6-6";
|
|
30
|
+
return /* @__PURE__ */ jsx2(Svg, { size, children: /* @__PURE__ */ jsx2("path", { d }) });
|
|
31
|
+
}
|
|
32
|
+
var DownloadGlyph = fromElement(Download);
|
|
33
|
+
function PlugGlyph({ size } = {}) {
|
|
34
|
+
return /* @__PURE__ */ jsxs(Svg, { size, children: [
|
|
35
|
+
/* @__PURE__ */ jsx2("path", { d: "M12 22v-5" }),
|
|
36
|
+
/* @__PURE__ */ jsx2("path", { d: "M9 8V2M15 8V2" }),
|
|
37
|
+
/* @__PURE__ */ jsx2("path", { d: "M18 8v3a6 6 0 0 1-12 0V8Z" })
|
|
38
|
+
] });
|
|
39
|
+
}
|
|
40
|
+
function SortAscGlyph({ size = 9 } = {}) {
|
|
41
|
+
return /* @__PURE__ */ jsxs(Svg, { size, children: [
|
|
42
|
+
/* @__PURE__ */ jsx2("path", { d: "m5 12 7-7 7 7" }),
|
|
43
|
+
/* @__PURE__ */ jsx2("path", { d: "M12 19V5" })
|
|
44
|
+
] });
|
|
45
|
+
}
|
|
46
|
+
function SortDescGlyph({ size = 9 } = {}) {
|
|
47
|
+
return /* @__PURE__ */ jsxs(Svg, { size, children: [
|
|
48
|
+
/* @__PURE__ */ jsx2("path", { d: "M12 5v14" }),
|
|
49
|
+
/* @__PURE__ */ jsx2("path", { d: "m19 12-7 7-7-7" })
|
|
50
|
+
] });
|
|
51
|
+
}
|
|
52
|
+
function ExpandGlyph({ size } = {}) {
|
|
53
|
+
return /* @__PURE__ */ jsxs(Svg, { size, children: [
|
|
54
|
+
/* @__PURE__ */ jsx2("path", { d: "M12 22v-6" }),
|
|
55
|
+
/* @__PURE__ */ jsx2("path", { d: "M12 8V2" }),
|
|
56
|
+
/* @__PURE__ */ jsx2("path", { d: "m9 5 3-3 3 3" }),
|
|
57
|
+
/* @__PURE__ */ jsx2("path", { d: "m9 19 3 3 3-3" }),
|
|
58
|
+
/* @__PURE__ */ jsx2("path", { d: "M4 12H2" }),
|
|
59
|
+
/* @__PURE__ */ jsx2("path", { d: "M10 12H8" }),
|
|
60
|
+
/* @__PURE__ */ jsx2("path", { d: "M16 12h-2" }),
|
|
61
|
+
/* @__PURE__ */ jsx2("path", { d: "M22 12h-2" })
|
|
62
|
+
] });
|
|
63
|
+
}
|
|
64
|
+
function CollapseGlyph({ size } = {}) {
|
|
65
|
+
return /* @__PURE__ */ jsxs(Svg, { size, children: [
|
|
66
|
+
/* @__PURE__ */ jsx2("path", { d: "M12 22v-6" }),
|
|
67
|
+
/* @__PURE__ */ jsx2("path", { d: "M12 8V2" }),
|
|
68
|
+
/* @__PURE__ */ jsx2("path", { d: "m9 13 3 3 3-3" }),
|
|
69
|
+
/* @__PURE__ */ jsx2("path", { d: "m9 11 3-3 3 3" }),
|
|
70
|
+
/* @__PURE__ */ jsx2("path", { d: "M4 12H2" }),
|
|
71
|
+
/* @__PURE__ */ jsx2("path", { d: "M22 12h-2" })
|
|
72
|
+
] });
|
|
73
|
+
}
|
|
74
|
+
var CopyGlyph = fromElement(Copy);
|
|
75
|
+
function ClearGlyph({ size } = {}) {
|
|
76
|
+
return /* @__PURE__ */ jsxs(Svg, { size, children: [
|
|
77
|
+
/* @__PURE__ */ jsx2("rect", { x: "3", y: "3", width: "18", height: "18", rx: "3" }),
|
|
78
|
+
/* @__PURE__ */ jsx2("path", { d: "m6 6 12 12" })
|
|
79
|
+
] });
|
|
80
|
+
}
|
|
81
|
+
function CancelGlyph({ size } = {}) {
|
|
82
|
+
return /* @__PURE__ */ jsx2(Svg, { size, children: /* @__PURE__ */ jsx2("rect", { x: "5", y: "5", width: "14", height: "14", rx: "2" }) });
|
|
83
|
+
}
|
|
84
|
+
function RetryGlyph({ size } = {}) {
|
|
85
|
+
return /* @__PURE__ */ jsxs(Svg, { size, children: [
|
|
86
|
+
/* @__PURE__ */ jsx2("path", { d: "M21 12a9 9 0 1 1-3.36-7" }),
|
|
87
|
+
/* @__PURE__ */ jsx2("path", { d: "M21 3v6h-6" })
|
|
88
|
+
] });
|
|
89
|
+
}
|
|
90
|
+
function CaseGlyph({ size = 12 } = {}) {
|
|
91
|
+
return /* @__PURE__ */ jsxs(Svg, { size, children: [
|
|
92
|
+
/* @__PURE__ */ jsx2("path", { d: "m3 15 4-8 4 8" }),
|
|
93
|
+
/* @__PURE__ */ jsx2("path", { d: "M4 13h6" }),
|
|
94
|
+
/* @__PURE__ */ jsx2("rect", { x: "15", y: "9", width: "6", height: "6", rx: "1.5" }),
|
|
95
|
+
/* @__PURE__ */ jsx2("path", { d: "M21 9v6" })
|
|
96
|
+
] });
|
|
97
|
+
}
|
|
98
|
+
function WholeWordGlyph({ size = 12 } = {}) {
|
|
99
|
+
return /* @__PURE__ */ jsxs(Svg, { size, children: [
|
|
100
|
+
/* @__PURE__ */ jsx2("path", { d: "M7 12h10" }),
|
|
101
|
+
/* @__PURE__ */ jsx2("path", { d: "M3 7v10" }),
|
|
102
|
+
/* @__PURE__ */ jsx2("path", { d: "M21 7v10" })
|
|
103
|
+
] });
|
|
104
|
+
}
|
|
105
|
+
function RegexGlyph({ size = 12 } = {}) {
|
|
106
|
+
return /* @__PURE__ */ jsxs(Svg, { size, children: [
|
|
107
|
+
/* @__PURE__ */ jsx2("path", { d: "M12 4v8" }),
|
|
108
|
+
/* @__PURE__ */ jsx2("path", { d: "m8.5 6 7 4" }),
|
|
109
|
+
/* @__PURE__ */ jsx2("path", { d: "m8.5 10 7-4" }),
|
|
110
|
+
/* @__PURE__ */ jsx2("path", { d: "M5 18h2" })
|
|
111
|
+
] });
|
|
112
|
+
}
|
|
113
|
+
function AlignLeftGlyph({ size } = {}) {
|
|
114
|
+
return /* @__PURE__ */ jsxs(Svg, { size, children: [
|
|
115
|
+
/* @__PURE__ */ jsx2("rect", { x: "6", y: "14", width: "9", height: "6", rx: "2" }),
|
|
116
|
+
/* @__PURE__ */ jsx2("rect", { x: "6", y: "4", width: "16", height: "6", rx: "2" }),
|
|
117
|
+
/* @__PURE__ */ jsx2("path", { d: "M2 2v20" })
|
|
118
|
+
] });
|
|
119
|
+
}
|
|
120
|
+
function AlignHCenterGlyph({ size } = {}) {
|
|
121
|
+
return /* @__PURE__ */ jsxs(Svg, { size, children: [
|
|
122
|
+
/* @__PURE__ */ jsx2("path", { d: "M12 2v20" }),
|
|
123
|
+
/* @__PURE__ */ jsx2("path", { d: "M8 10H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h4" }),
|
|
124
|
+
/* @__PURE__ */ jsx2("path", { d: "M16 10h4a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2h-4" }),
|
|
125
|
+
/* @__PURE__ */ jsx2("path", { d: "M8 20H7a2 2 0 0 1-2-2v-2a2 2 0 0 1 2-2h1" }),
|
|
126
|
+
/* @__PURE__ */ jsx2("path", { d: "M16 14h1a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2h-1" })
|
|
127
|
+
] });
|
|
128
|
+
}
|
|
129
|
+
function AlignRightGlyph({ size } = {}) {
|
|
130
|
+
return /* @__PURE__ */ jsxs(Svg, { size, children: [
|
|
131
|
+
/* @__PURE__ */ jsx2("rect", { x: "2", y: "4", width: "16", height: "6", rx: "2" }),
|
|
132
|
+
/* @__PURE__ */ jsx2("rect", { x: "9", y: "14", width: "9", height: "6", rx: "2" }),
|
|
133
|
+
/* @__PURE__ */ jsx2("path", { d: "M22 22V2" })
|
|
134
|
+
] });
|
|
135
|
+
}
|
|
136
|
+
function AlignTopGlyph({ size } = {}) {
|
|
137
|
+
return /* @__PURE__ */ jsxs(Svg, { size, children: [
|
|
138
|
+
/* @__PURE__ */ jsx2("rect", { x: "4", y: "6", width: "6", height: "16", rx: "2" }),
|
|
139
|
+
/* @__PURE__ */ jsx2("rect", { x: "14", y: "6", width: "6", height: "9", rx: "2" }),
|
|
140
|
+
/* @__PURE__ */ jsx2("path", { d: "M22 2H2" })
|
|
141
|
+
] });
|
|
142
|
+
}
|
|
143
|
+
function AlignVCenterGlyph({ size } = {}) {
|
|
144
|
+
return /* @__PURE__ */ jsxs(Svg, { size, children: [
|
|
145
|
+
/* @__PURE__ */ jsx2("path", { d: "M2 12h20" }),
|
|
146
|
+
/* @__PURE__ */ jsx2("path", { d: "M10 16v4a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-4" }),
|
|
147
|
+
/* @__PURE__ */ jsx2("path", { d: "M10 8V4a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v4" }),
|
|
148
|
+
/* @__PURE__ */ jsx2("path", { d: "M20 16v1a2 2 0 0 1-2 2h-2a2 2 0 0 1-2-2v-1" }),
|
|
149
|
+
/* @__PURE__ */ jsx2("path", { d: "M14 8V7c0-1.1.9-2 2-2h2a2 2 0 0 1 2 2v1" })
|
|
150
|
+
] });
|
|
151
|
+
}
|
|
152
|
+
function AlignBottomGlyph({ size } = {}) {
|
|
153
|
+
return /* @__PURE__ */ jsxs(Svg, { size, children: [
|
|
154
|
+
/* @__PURE__ */ jsx2("rect", { x: "4", y: "2", width: "6", height: "16", rx: "2" }),
|
|
155
|
+
/* @__PURE__ */ jsx2("rect", { x: "14", y: "9", width: "6", height: "9", rx: "2" }),
|
|
156
|
+
/* @__PURE__ */ jsx2("path", { d: "M22 22H2" })
|
|
157
|
+
] });
|
|
158
|
+
}
|
|
159
|
+
function DistributeHGlyph({ size } = {}) {
|
|
160
|
+
return /* @__PURE__ */ jsxs(Svg, { size, children: [
|
|
161
|
+
/* @__PURE__ */ jsx2("rect", { x: "9", y: "9", width: "6", height: "6", rx: "2" }),
|
|
162
|
+
/* @__PURE__ */ jsx2("path", { d: "M4 22V2" }),
|
|
163
|
+
/* @__PURE__ */ jsx2("path", { d: "M20 22V2" })
|
|
164
|
+
] });
|
|
165
|
+
}
|
|
166
|
+
function DistributeVGlyph({ size } = {}) {
|
|
167
|
+
return /* @__PURE__ */ jsxs(Svg, { size, children: [
|
|
168
|
+
/* @__PURE__ */ jsx2("rect", { x: "9", y: "9", width: "6", height: "6", rx: "2" }),
|
|
169
|
+
/* @__PURE__ */ jsx2("path", { d: "M22 20H2" }),
|
|
170
|
+
/* @__PURE__ */ jsx2("path", { d: "M22 4H2" })
|
|
171
|
+
] });
|
|
172
|
+
}
|
|
173
|
+
function BringToFrontGlyph({ size } = {}) {
|
|
174
|
+
return /* @__PURE__ */ jsxs(Svg, { size, children: [
|
|
175
|
+
/* @__PURE__ */ jsx2("rect", { x: "8", y: "8", width: "8", height: "8", rx: "2" }),
|
|
176
|
+
/* @__PURE__ */ jsx2("path", { d: "M4 10a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2" }),
|
|
177
|
+
/* @__PURE__ */ jsx2("path", { d: "M14 20a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2" })
|
|
178
|
+
] });
|
|
179
|
+
}
|
|
180
|
+
function SendToBackGlyph({ size } = {}) {
|
|
181
|
+
return /* @__PURE__ */ jsxs(Svg, { size, children: [
|
|
182
|
+
/* @__PURE__ */ jsx2("rect", { x: "14", y: "14", width: "8", height: "8", rx: "2" }),
|
|
183
|
+
/* @__PURE__ */ jsx2("rect", { x: "2", y: "2", width: "8", height: "8", rx: "2" }),
|
|
184
|
+
/* @__PURE__ */ jsx2("path", { d: "M7 14v1a2 2 0 0 0 2 2h1" }),
|
|
185
|
+
/* @__PURE__ */ jsx2("path", { d: "M14 7h1a2 2 0 0 1 2 2v1" })
|
|
186
|
+
] });
|
|
187
|
+
}
|
|
188
|
+
function GroupGlyph({ size } = {}) {
|
|
189
|
+
return /* @__PURE__ */ jsxs(Svg, { size, children: [
|
|
190
|
+
/* @__PURE__ */ jsx2("path", { d: "M3 7V5a2 2 0 0 1 2-2h2" }),
|
|
191
|
+
/* @__PURE__ */ jsx2("path", { d: "M17 3h2a2 2 0 0 1 2 2v2" }),
|
|
192
|
+
/* @__PURE__ */ jsx2("path", { d: "M21 17v2a2 2 0 0 1-2 2h-2" }),
|
|
193
|
+
/* @__PURE__ */ jsx2("path", { d: "M7 21H5a2 2 0 0 1-2-2v-2" }),
|
|
194
|
+
/* @__PURE__ */ jsx2("rect", { x: "7", y: "7", width: "5", height: "5", rx: "1" }),
|
|
195
|
+
/* @__PURE__ */ jsx2("rect", { x: "12", y: "12", width: "5", height: "5", rx: "1" })
|
|
196
|
+
] });
|
|
197
|
+
}
|
|
198
|
+
function UngroupGlyph({ size } = {}) {
|
|
199
|
+
return /* @__PURE__ */ jsxs(Svg, { size, children: [
|
|
200
|
+
/* @__PURE__ */ jsx2("rect", { x: "2", y: "4", width: "8", height: "6", rx: "1" }),
|
|
201
|
+
/* @__PURE__ */ jsx2("rect", { x: "14", y: "14", width: "8", height: "6", rx: "1" })
|
|
202
|
+
] });
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export {
|
|
206
|
+
fromElement,
|
|
207
|
+
Svg,
|
|
208
|
+
RefreshGlyph,
|
|
209
|
+
XGlyph,
|
|
210
|
+
PlusGlyph,
|
|
211
|
+
MinusGlyph,
|
|
212
|
+
ChevronGlyph,
|
|
213
|
+
DownloadGlyph,
|
|
214
|
+
PlugGlyph,
|
|
215
|
+
SortAscGlyph,
|
|
216
|
+
SortDescGlyph,
|
|
217
|
+
ExpandGlyph,
|
|
218
|
+
CollapseGlyph,
|
|
219
|
+
CopyGlyph,
|
|
220
|
+
ClearGlyph,
|
|
221
|
+
CancelGlyph,
|
|
222
|
+
RetryGlyph,
|
|
223
|
+
CaseGlyph,
|
|
224
|
+
WholeWordGlyph,
|
|
225
|
+
RegexGlyph,
|
|
226
|
+
AlignLeftGlyph,
|
|
227
|
+
AlignHCenterGlyph,
|
|
228
|
+
AlignRightGlyph,
|
|
229
|
+
AlignTopGlyph,
|
|
230
|
+
AlignVCenterGlyph,
|
|
231
|
+
AlignBottomGlyph,
|
|
232
|
+
DistributeHGlyph,
|
|
233
|
+
DistributeVGlyph,
|
|
234
|
+
BringToFrontGlyph,
|
|
235
|
+
SendToBackGlyph,
|
|
236
|
+
GroupGlyph,
|
|
237
|
+
UngroupGlyph
|
|
238
|
+
};
|