@xenosystem/blocks 0.22.0 → 0.23.1
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 +451 -0
- package/dist/corpus/index.js +1005 -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 +221 -217
- package/dist/chunk-6ZIWULSK.js +0 -629
package/dist/edit/index.js
CHANGED
|
@@ -1,9 +1,31 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
DEFAULT_CURVE_ZOOM,
|
|
3
|
+
DEFAULT_GRAPH_ZOOM,
|
|
4
|
+
XenoCurveScale,
|
|
5
|
+
XenoGraphScale,
|
|
6
|
+
XenoNodeIndex,
|
|
7
|
+
boxesIntersect,
|
|
8
|
+
distanceToEdge,
|
|
9
|
+
edgeBounds,
|
|
10
|
+
edgeKey,
|
|
11
|
+
edgePath,
|
|
12
|
+
findPort,
|
|
13
|
+
fitToBounds,
|
|
14
|
+
fitValueRange,
|
|
15
|
+
graphBounds,
|
|
16
|
+
nodeBounds,
|
|
17
|
+
portHasRoom,
|
|
18
|
+
portRefId,
|
|
19
|
+
visibleEdges,
|
|
20
|
+
wheelZoomFactor,
|
|
21
|
+
zoomAboutCursor,
|
|
22
|
+
zoomTimeAboutCursor,
|
|
23
|
+
zoomValueAboutCursor
|
|
24
|
+
} from "../chunk-76V25B3E.js";
|
|
25
|
+
import {
|
|
3
26
|
CopyIcon,
|
|
4
27
|
DiamondIcon,
|
|
5
28
|
EntryIcon,
|
|
6
|
-
ExpandGlyph,
|
|
7
29
|
EyedropperIcon,
|
|
8
30
|
ICONS,
|
|
9
31
|
KindGlyph,
|
|
@@ -13,7 +35,6 @@ import {
|
|
|
13
35
|
NextIcon,
|
|
14
36
|
PrevIcon,
|
|
15
37
|
RedoIcon,
|
|
16
|
-
RefreshGlyph,
|
|
17
38
|
ResetIcon,
|
|
18
39
|
StopwatchIcon,
|
|
19
40
|
SwapIcon,
|
|
@@ -23,7 +44,12 @@ import {
|
|
|
23
44
|
VisibilityIcon,
|
|
24
45
|
XIcon,
|
|
25
46
|
XIcon2
|
|
26
|
-
} from "../chunk-
|
|
47
|
+
} from "../chunk-YZ6J6A67.js";
|
|
48
|
+
import {
|
|
49
|
+
CollapseGlyph,
|
|
50
|
+
ExpandGlyph,
|
|
51
|
+
RefreshGlyph
|
|
52
|
+
} from "../chunk-YSMDUREC.js";
|
|
27
53
|
import "../chunk-2KG3PWR4.js";
|
|
28
54
|
|
|
29
55
|
// src/edit/layers/panel.ts
|
|
@@ -6586,180 +6612,6 @@ import {
|
|
|
6586
6612
|
// src/edit/curve/controller.ts
|
|
6587
6613
|
import { createThrottle as createThrottle5, DEFAULT_TIMEBASE } from "@xenosystem/time-core";
|
|
6588
6614
|
|
|
6589
|
-
// src/edit/curve/scale.ts
|
|
6590
|
-
var DEFAULT_CURVE_ZOOM = { min: 5e-4, max: 64 };
|
|
6591
|
-
var XenoCurveScale = class _XenoCurveScale {
|
|
6592
|
-
pxPerTick;
|
|
6593
|
-
scrollTick;
|
|
6594
|
-
minValue;
|
|
6595
|
-
maxValue;
|
|
6596
|
-
widthPx;
|
|
6597
|
-
heightPx;
|
|
6598
|
-
gutterPx;
|
|
6599
|
-
rulerPx;
|
|
6600
|
-
constructor(state, geometry) {
|
|
6601
|
-
this.widthPx = Math.max(1, finiteOr(geometry.widthPx, 1));
|
|
6602
|
-
this.heightPx = Math.max(1, finiteOr(geometry.heightPx, 1));
|
|
6603
|
-
this.gutterPx = Math.max(0, Math.min(finiteOr(geometry.gutterPx, 0), this.widthPx - 1));
|
|
6604
|
-
this.rulerPx = Math.max(0, Math.min(finiteOr(geometry.rulerPx, 0), this.heightPx - 1));
|
|
6605
|
-
this.pxPerTick = Math.max(Number.MIN_VALUE, finiteOr(state.pxPerTick, 1));
|
|
6606
|
-
this.scrollTick = finiteOr(state.scrollTick, 0);
|
|
6607
|
-
const min = finiteOr(state.minValue, 0);
|
|
6608
|
-
const max = finiteOr(state.maxValue, 1);
|
|
6609
|
-
if (max - min < 1e-12) {
|
|
6610
|
-
const centre = (max + min) / 2;
|
|
6611
|
-
this.minValue = centre - 0.5;
|
|
6612
|
-
this.maxValue = centre + 0.5;
|
|
6613
|
-
} else {
|
|
6614
|
-
this.minValue = min;
|
|
6615
|
-
this.maxValue = max;
|
|
6616
|
-
}
|
|
6617
|
-
}
|
|
6618
|
-
/** Width of the plot area. Always ≥ 1. */
|
|
6619
|
-
get plotPx() {
|
|
6620
|
-
return Math.max(1, this.widthPx - this.gutterPx);
|
|
6621
|
-
}
|
|
6622
|
-
/** Height of the plot area. Always ≥ 1. */
|
|
6623
|
-
get plotHeightPx() {
|
|
6624
|
-
return Math.max(1, this.heightPx - this.rulerPx);
|
|
6625
|
-
}
|
|
6626
|
-
/** Ticks visible. */
|
|
6627
|
-
get spanTicks() {
|
|
6628
|
-
return this.plotPx / this.pxPerTick;
|
|
6629
|
-
}
|
|
6630
|
-
/** Tick at the right edge. */
|
|
6631
|
-
get endTick() {
|
|
6632
|
-
return this.scrollTick + this.spanTicks;
|
|
6633
|
-
}
|
|
6634
|
-
/** Value units per pixel. */
|
|
6635
|
-
get valuePerPx() {
|
|
6636
|
-
return (this.maxValue - this.minValue) / this.plotHeightPx;
|
|
6637
|
-
}
|
|
6638
|
-
/** Container-relative x → tick. */
|
|
6639
|
-
xToTick(x) {
|
|
6640
|
-
return this.scrollTick + (finiteOr(x, 0) - this.gutterPx) / this.pxPerTick;
|
|
6641
|
-
}
|
|
6642
|
-
/** Tick → container-relative x. */
|
|
6643
|
-
tickToX(tick) {
|
|
6644
|
-
return this.gutterPx + (finiteOr(tick, 0) - this.scrollTick) * this.pxPerTick;
|
|
6645
|
-
}
|
|
6646
|
-
/**
|
|
6647
|
-
* Container-relative y → value.
|
|
6648
|
-
*
|
|
6649
|
-
* **Inverted.** Screen y grows downward, value grows upward. Every sign error in a graph editor
|
|
6650
|
-
* lives on this one line.
|
|
6651
|
-
*/
|
|
6652
|
-
yToValue(y) {
|
|
6653
|
-
const local = finiteOr(y, 0) - this.rulerPx;
|
|
6654
|
-
return this.maxValue - local / this.plotHeightPx * (this.maxValue - this.minValue);
|
|
6655
|
-
}
|
|
6656
|
-
/** Value → container-relative y. */
|
|
6657
|
-
valueToY(value) {
|
|
6658
|
-
const fraction = (this.maxValue - finiteOr(value, 0)) / (this.maxValue - this.minValue);
|
|
6659
|
-
return this.rulerPx + fraction * this.plotHeightPx;
|
|
6660
|
-
}
|
|
6661
|
-
/**
|
|
6662
|
-
* A tick DURATION → a pixel width.
|
|
6663
|
-
*
|
|
6664
|
-
* Separate from {@link tickToX}, because rounding is not associative: mixing
|
|
6665
|
-
* `durationToPx(b - a)` with `tickToX(b) - tickToX(a)` puts a curve's handle a pixel off its key
|
|
6666
|
-
* at some zoom levels and not others.
|
|
6667
|
-
*/
|
|
6668
|
-
durationToPx(ticks) {
|
|
6669
|
-
return finiteOr(ticks, 0) * this.pxPerTick;
|
|
6670
|
-
}
|
|
6671
|
-
/** A value DELTA → a pixel height. Sign-preserving and NOT inverted. */
|
|
6672
|
-
valueDeltaToPx(delta) {
|
|
6673
|
-
return finiteOr(delta, 0) / (this.maxValue - this.minValue) * this.plotHeightPx;
|
|
6674
|
-
}
|
|
6675
|
-
/** A pixel distance → a tick distance. For screen-space thresholds. */
|
|
6676
|
-
pxToTicks(px) {
|
|
6677
|
-
return finiteOr(px, 0) / this.pxPerTick;
|
|
6678
|
-
}
|
|
6679
|
-
/** A pixel distance → a value distance. For screen-space thresholds. */
|
|
6680
|
-
pxToValue(px) {
|
|
6681
|
-
return finiteOr(px, 0) * this.valuePerPx;
|
|
6682
|
-
}
|
|
6683
|
-
/** Is this point inside the plot area? */
|
|
6684
|
-
isInPlot(x, y) {
|
|
6685
|
-
return x >= this.gutterPx && x <= this.widthPx && y >= this.rulerPx && y <= this.heightPx;
|
|
6686
|
-
}
|
|
6687
|
-
/** A copy with new state. */
|
|
6688
|
-
with(state) {
|
|
6689
|
-
return new _XenoCurveScale(
|
|
6690
|
-
{
|
|
6691
|
-
pxPerTick: state.pxPerTick ?? this.pxPerTick,
|
|
6692
|
-
scrollTick: state.scrollTick ?? this.scrollTick,
|
|
6693
|
-
minValue: state.minValue ?? this.minValue,
|
|
6694
|
-
maxValue: state.maxValue ?? this.maxValue
|
|
6695
|
-
},
|
|
6696
|
-
{
|
|
6697
|
-
widthPx: this.widthPx,
|
|
6698
|
-
heightPx: this.heightPx,
|
|
6699
|
-
gutterPx: this.gutterPx,
|
|
6700
|
-
rulerPx: this.rulerPx
|
|
6701
|
-
}
|
|
6702
|
-
);
|
|
6703
|
-
}
|
|
6704
|
-
/** A copy with new geometry. */
|
|
6705
|
-
resized(geometry) {
|
|
6706
|
-
return new _XenoCurveScale(
|
|
6707
|
-
{
|
|
6708
|
-
pxPerTick: this.pxPerTick,
|
|
6709
|
-
scrollTick: this.scrollTick,
|
|
6710
|
-
minValue: this.minValue,
|
|
6711
|
-
maxValue: this.maxValue
|
|
6712
|
-
},
|
|
6713
|
-
{
|
|
6714
|
-
widthPx: geometry.widthPx ?? this.widthPx,
|
|
6715
|
-
heightPx: geometry.heightPx ?? this.heightPx,
|
|
6716
|
-
gutterPx: geometry.gutterPx ?? this.gutterPx,
|
|
6717
|
-
rulerPx: geometry.rulerPx ?? this.rulerPx
|
|
6718
|
-
}
|
|
6719
|
-
);
|
|
6720
|
-
}
|
|
6721
|
-
};
|
|
6722
|
-
function finiteOr(value, fallback) {
|
|
6723
|
-
return Number.isFinite(value) ? value : fallback;
|
|
6724
|
-
}
|
|
6725
|
-
function zoomTimeAboutCursor(scale, x, factor, bounds = DEFAULT_CURVE_ZOOM) {
|
|
6726
|
-
const anchorTick = scale.xToTick(x);
|
|
6727
|
-
const requested = scale.pxPerTick * (Number.isFinite(factor) && factor > 0 ? factor : 1);
|
|
6728
|
-
const pxPerTick = Math.min(bounds.max, Math.max(bounds.min, requested));
|
|
6729
|
-
return {
|
|
6730
|
-
pxPerTick,
|
|
6731
|
-
scrollTick: anchorTick - (x - scale.gutterPx) / pxPerTick,
|
|
6732
|
-
minValue: scale.minValue,
|
|
6733
|
-
maxValue: scale.maxValue
|
|
6734
|
-
};
|
|
6735
|
-
}
|
|
6736
|
-
function zoomValueAboutCursor(scale, y, factor) {
|
|
6737
|
-
const anchorValue = scale.yToValue(y);
|
|
6738
|
-
const safeFactor = Number.isFinite(factor) && factor > 0 ? factor : 1;
|
|
6739
|
-
const span = (scale.maxValue - scale.minValue) / safeFactor;
|
|
6740
|
-
const fraction = (scale.maxValue - anchorValue) / (scale.maxValue - scale.minValue);
|
|
6741
|
-
const maxValue = anchorValue + span * fraction;
|
|
6742
|
-
return {
|
|
6743
|
-
pxPerTick: scale.pxPerTick,
|
|
6744
|
-
scrollTick: scale.scrollTick,
|
|
6745
|
-
minValue: maxValue - span,
|
|
6746
|
-
maxValue
|
|
6747
|
-
};
|
|
6748
|
-
}
|
|
6749
|
-
function wheelZoomFactor(deltaY, sensitivity = 0.01) {
|
|
6750
|
-
const raw = Number.isFinite(deltaY) ? deltaY : 0;
|
|
6751
|
-
const clamped = Math.max(-10, Math.min(10, raw));
|
|
6752
|
-
return Math.pow(2, -clamped * sensitivity * 10);
|
|
6753
|
-
}
|
|
6754
|
-
function fitValueRange(extent, marginFraction = 0.1) {
|
|
6755
|
-
if (!extent || !Number.isFinite(extent.min) || !Number.isFinite(extent.max)) {
|
|
6756
|
-
return { minValue: 0, maxValue: 1 };
|
|
6757
|
-
}
|
|
6758
|
-
const span = extent.max - extent.min;
|
|
6759
|
-
const margin = Math.max(span * marginFraction, 0.5);
|
|
6760
|
-
return { minValue: extent.min - margin, maxValue: extent.max + margin };
|
|
6761
|
-
}
|
|
6762
|
-
|
|
6763
6615
|
// src/edit/curve/bezier.ts
|
|
6764
6616
|
function cubic(t, p1, p2) {
|
|
6765
6617
|
const mt = 1 - t;
|
|
@@ -10933,243 +10785,6 @@ import {
|
|
|
10933
10785
|
// src/edit/nodegraph/throttle.ts
|
|
10934
10786
|
import { createThrottle as createThrottle6 } from "@xenosystem/tree-core";
|
|
10935
10787
|
|
|
10936
|
-
// src/edit/nodegraph/scale.ts
|
|
10937
|
-
var DEFAULT_GRAPH_ZOOM = { min: 0.05, max: 4 };
|
|
10938
|
-
var XenoGraphScale = class _XenoGraphScale {
|
|
10939
|
-
zoom;
|
|
10940
|
-
x;
|
|
10941
|
-
y;
|
|
10942
|
-
widthPx;
|
|
10943
|
-
heightPx;
|
|
10944
|
-
constructor(state, geometry) {
|
|
10945
|
-
this.widthPx = Math.max(1, finiteOr2(geometry.widthPx, 1));
|
|
10946
|
-
this.heightPx = Math.max(1, finiteOr2(geometry.heightPx, 1));
|
|
10947
|
-
this.zoom = Math.max(Number.MIN_VALUE, finiteOr2(state.zoom, 1));
|
|
10948
|
-
this.x = finiteOr2(state.x, 0);
|
|
10949
|
-
this.y = finiteOr2(state.y, 0);
|
|
10950
|
-
}
|
|
10951
|
-
/** Graph-space width of the viewport. */
|
|
10952
|
-
get graphWidth() {
|
|
10953
|
-
return this.widthPx / this.zoom;
|
|
10954
|
-
}
|
|
10955
|
-
/** Graph-space height of the viewport. */
|
|
10956
|
-
get graphHeight() {
|
|
10957
|
-
return this.heightPx / this.zoom;
|
|
10958
|
-
}
|
|
10959
|
-
/** The visible rectangle in graph space. */
|
|
10960
|
-
get viewport() {
|
|
10961
|
-
return { x: this.x, y: this.y, width: this.graphWidth, height: this.graphHeight };
|
|
10962
|
-
}
|
|
10963
|
-
/** Container-relative screen point → graph point. */
|
|
10964
|
-
screenToGraph(px, py) {
|
|
10965
|
-
return { x: this.x + finiteOr2(px, 0) / this.zoom, y: this.y + finiteOr2(py, 0) / this.zoom };
|
|
10966
|
-
}
|
|
10967
|
-
/** Graph point → container-relative screen point. */
|
|
10968
|
-
graphToScreen(gx, gy) {
|
|
10969
|
-
return { x: (finiteOr2(gx, 0) - this.x) * this.zoom, y: (finiteOr2(gy, 0) - this.y) * this.zoom };
|
|
10970
|
-
}
|
|
10971
|
-
/** A graph-space distance → pixels. */
|
|
10972
|
-
toPx(distance) {
|
|
10973
|
-
return finiteOr2(distance, 0) * this.zoom;
|
|
10974
|
-
}
|
|
10975
|
-
/** A pixel distance → graph space. Used for screen-space hit thresholds. */
|
|
10976
|
-
toGraph(px) {
|
|
10977
|
-
return finiteOr2(px, 0) / this.zoom;
|
|
10978
|
-
}
|
|
10979
|
-
/** A copy with new state. */
|
|
10980
|
-
with(state) {
|
|
10981
|
-
return new _XenoGraphScale(
|
|
10982
|
-
{ zoom: state.zoom ?? this.zoom, x: state.x ?? this.x, y: state.y ?? this.y },
|
|
10983
|
-
{ widthPx: this.widthPx, heightPx: this.heightPx }
|
|
10984
|
-
);
|
|
10985
|
-
}
|
|
10986
|
-
/** A copy with new geometry. */
|
|
10987
|
-
resized(geometry) {
|
|
10988
|
-
return new _XenoGraphScale(
|
|
10989
|
-
{ zoom: this.zoom, x: this.x, y: this.y },
|
|
10990
|
-
{ widthPx: geometry.widthPx ?? this.widthPx, heightPx: geometry.heightPx ?? this.heightPx }
|
|
10991
|
-
);
|
|
10992
|
-
}
|
|
10993
|
-
};
|
|
10994
|
-
function finiteOr2(value, fallback) {
|
|
10995
|
-
return Number.isFinite(value) ? value : fallback;
|
|
10996
|
-
}
|
|
10997
|
-
function zoomAboutCursor(scale, px, py, factor, bounds = DEFAULT_GRAPH_ZOOM) {
|
|
10998
|
-
const anchor = scale.screenToGraph(px, py);
|
|
10999
|
-
const requested = scale.zoom * (Number.isFinite(factor) && factor > 0 ? factor : 1);
|
|
11000
|
-
const zoom = Math.min(bounds.max, Math.max(bounds.min, requested));
|
|
11001
|
-
return { zoom, x: anchor.x - px / zoom, y: anchor.y - py / zoom };
|
|
11002
|
-
}
|
|
11003
|
-
function fitToBounds(bounds, widthPx, heightPx, zoomBounds = DEFAULT_GRAPH_ZOOM, paddingPx = 40) {
|
|
11004
|
-
if (!bounds || bounds.width <= 0 || bounds.height <= 0) return { zoom: 1, x: 0, y: 0 };
|
|
11005
|
-
const usableW = Math.max(1, widthPx - paddingPx * 2);
|
|
11006
|
-
const usableH = Math.max(1, heightPx - paddingPx * 2);
|
|
11007
|
-
const zoom = Math.min(
|
|
11008
|
-
zoomBounds.max,
|
|
11009
|
-
Math.max(zoomBounds.min, Math.min(usableW / bounds.width, usableH / bounds.height))
|
|
11010
|
-
);
|
|
11011
|
-
const x = bounds.x + bounds.width / 2 - widthPx / zoom / 2;
|
|
11012
|
-
const y = bounds.y + bounds.height / 2 - heightPx / zoom / 2;
|
|
11013
|
-
return { zoom, x, y };
|
|
11014
|
-
}
|
|
11015
|
-
function graphBounds(nodes) {
|
|
11016
|
-
if (nodes.length === 0) return null;
|
|
11017
|
-
let minX = Number.POSITIVE_INFINITY;
|
|
11018
|
-
let minY = Number.POSITIVE_INFINITY;
|
|
11019
|
-
let maxX = Number.NEGATIVE_INFINITY;
|
|
11020
|
-
let maxY = Number.NEGATIVE_INFINITY;
|
|
11021
|
-
for (const node of nodes) {
|
|
11022
|
-
if (!Number.isFinite(node.x) || !Number.isFinite(node.y)) continue;
|
|
11023
|
-
minX = Math.min(minX, node.x);
|
|
11024
|
-
minY = Math.min(minY, node.y);
|
|
11025
|
-
maxX = Math.max(maxX, node.x + Math.max(1, node.width));
|
|
11026
|
-
maxY = Math.max(maxY, node.y + Math.max(1, node.height));
|
|
11027
|
-
}
|
|
11028
|
-
if (!Number.isFinite(minX)) return null;
|
|
11029
|
-
return { x: minX, y: minY, width: Math.max(1, maxX - minX), height: Math.max(1, maxY - minY) };
|
|
11030
|
-
}
|
|
11031
|
-
|
|
11032
|
-
// src/edit/nodegraph/types.ts
|
|
11033
|
-
function portRefId(ref) {
|
|
11034
|
-
return `${ref.nodeId}${ref.portId}`;
|
|
11035
|
-
}
|
|
11036
|
-
function edgeKey(edge) {
|
|
11037
|
-
return `${edge.fromNodeId}${edge.fromPortId}${edge.toNodeId}${edge.toPortId}`;
|
|
11038
|
-
}
|
|
11039
|
-
function findPort(node, portId) {
|
|
11040
|
-
return node.inputs.find((p) => p.id === portId) ?? node.outputs.find((p) => p.id === portId) ?? null;
|
|
11041
|
-
}
|
|
11042
|
-
function portHasRoom(port, existingCount) {
|
|
11043
|
-
if (port.multiple !== void 0) return port.multiple || existingCount === 0;
|
|
11044
|
-
return port.direction === "out" ? true : existingCount === 0;
|
|
11045
|
-
}
|
|
11046
|
-
function nodeBounds(node) {
|
|
11047
|
-
return { x: node.x, y: node.y, width: Math.max(1, node.width), height: Math.max(1, node.height) };
|
|
11048
|
-
}
|
|
11049
|
-
function boxesIntersect(a, b) {
|
|
11050
|
-
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;
|
|
11051
|
-
}
|
|
11052
|
-
|
|
11053
|
-
// src/edit/nodegraph/spatial.ts
|
|
11054
|
-
var XenoNodeIndex = class {
|
|
11055
|
-
cellSize;
|
|
11056
|
-
cells = /* @__PURE__ */ new Map();
|
|
11057
|
-
all;
|
|
11058
|
-
constructor(nodes) {
|
|
11059
|
-
this.all = nodes;
|
|
11060
|
-
this.cellSize = chooseCellSize(nodes);
|
|
11061
|
-
for (const node of nodes) {
|
|
11062
|
-
const x0 = Math.floor(node.x / this.cellSize);
|
|
11063
|
-
const y0 = Math.floor(node.y / this.cellSize);
|
|
11064
|
-
const x1 = Math.floor((node.x + Math.max(1, node.width)) / this.cellSize);
|
|
11065
|
-
const y1 = Math.floor((node.y + Math.max(1, node.height)) / this.cellSize);
|
|
11066
|
-
for (let cx = x0; cx <= x1; cx += 1) {
|
|
11067
|
-
for (let cy = y0; cy <= y1; cy += 1) {
|
|
11068
|
-
const key = `${cx},${cy}`;
|
|
11069
|
-
const bucket = this.cells.get(key);
|
|
11070
|
-
if (bucket) bucket.push(node);
|
|
11071
|
-
else this.cells.set(key, [node]);
|
|
11072
|
-
}
|
|
11073
|
-
}
|
|
11074
|
-
}
|
|
11075
|
-
}
|
|
11076
|
-
/** Bucket size, for tests. */
|
|
11077
|
-
get bucketSize() {
|
|
11078
|
-
return this.cellSize;
|
|
11079
|
-
}
|
|
11080
|
-
/**
|
|
11081
|
-
* Nodes intersecting a box.
|
|
11082
|
-
*
|
|
11083
|
-
* @param box - The query rectangle in graph space.
|
|
11084
|
-
* @returns Matching nodes, each at most once.
|
|
11085
|
-
*/
|
|
11086
|
-
query(box) {
|
|
11087
|
-
const x0 = Math.floor(box.x / this.cellSize);
|
|
11088
|
-
const y0 = Math.floor(box.y / this.cellSize);
|
|
11089
|
-
const x1 = Math.floor((box.x + box.width) / this.cellSize);
|
|
11090
|
-
const y1 = Math.floor((box.y + box.height) / this.cellSize);
|
|
11091
|
-
const seen = /* @__PURE__ */ new Set();
|
|
11092
|
-
const out = [];
|
|
11093
|
-
for (let cx = x0; cx <= x1; cx += 1) {
|
|
11094
|
-
for (let cy = y0; cy <= y1; cy += 1) {
|
|
11095
|
-
for (const node of this.cells.get(`${cx},${cy}`) ?? []) {
|
|
11096
|
-
if (seen.has(node.id)) continue;
|
|
11097
|
-
seen.add(node.id);
|
|
11098
|
-
if (boxesIntersect(node, { ...box, width: Math.max(0, box.width), height: Math.max(0, box.height) })) {
|
|
11099
|
-
out.push(node);
|
|
11100
|
-
}
|
|
11101
|
-
}
|
|
11102
|
-
}
|
|
11103
|
-
}
|
|
11104
|
-
return out;
|
|
11105
|
-
}
|
|
11106
|
-
/** The topmost node containing a point, or `null`. */
|
|
11107
|
-
hit(x, y) {
|
|
11108
|
-
const candidates = this.query({ x, y, width: 1e-3, height: 1e-3 });
|
|
11109
|
-
if (candidates.length === 0) return null;
|
|
11110
|
-
let best = null;
|
|
11111
|
-
for (const node of candidates) {
|
|
11112
|
-
if (x >= node.x && x <= node.x + node.width && y >= node.y && y <= node.y + node.height) {
|
|
11113
|
-
if (!best || this.all.indexOf(node) > this.all.indexOf(best)) best = node;
|
|
11114
|
-
}
|
|
11115
|
-
}
|
|
11116
|
-
return best;
|
|
11117
|
-
}
|
|
11118
|
-
};
|
|
11119
|
-
function chooseCellSize(nodes) {
|
|
11120
|
-
if (nodes.length === 0) return 256;
|
|
11121
|
-
const sizes = [];
|
|
11122
|
-
for (const node of nodes) {
|
|
11123
|
-
const size = Math.max(node.width || 1, node.height || 1);
|
|
11124
|
-
if (Number.isFinite(size) && size > 0) sizes.push(size);
|
|
11125
|
-
}
|
|
11126
|
-
if (sizes.length === 0) return 256;
|
|
11127
|
-
sizes.sort((a, b) => a - b);
|
|
11128
|
-
const median = sizes[Math.floor(sizes.length / 2)];
|
|
11129
|
-
return Math.max(32, Math.min(4096, median * 2));
|
|
11130
|
-
}
|
|
11131
|
-
function edgeBounds(from, to, slack = 64) {
|
|
11132
|
-
const x = Math.min(from.x, to.x) - slack;
|
|
11133
|
-
const y = Math.min(from.y, to.y) - slack;
|
|
11134
|
-
return {
|
|
11135
|
-
x,
|
|
11136
|
-
y,
|
|
11137
|
-
width: Math.abs(to.x - from.x) + slack * 2,
|
|
11138
|
-
height: Math.abs(to.y - from.y) + slack * 2
|
|
11139
|
-
};
|
|
11140
|
-
}
|
|
11141
|
-
function visibleEdges(edges, portPosition, viewport) {
|
|
11142
|
-
const out = [];
|
|
11143
|
-
for (const edge of edges) {
|
|
11144
|
-
const from = portPosition(edge.fromNodeId, edge.fromPortId);
|
|
11145
|
-
const to = portPosition(edge.toNodeId, edge.toPortId);
|
|
11146
|
-
if (!from || !to) continue;
|
|
11147
|
-
if (!boxesIntersect(edgeBounds(from, to), viewport)) continue;
|
|
11148
|
-
out.push({ edge, from, to });
|
|
11149
|
-
}
|
|
11150
|
-
return out;
|
|
11151
|
-
}
|
|
11152
|
-
function edgePath(from, to) {
|
|
11153
|
-
const dx = Math.abs(to.x - from.x);
|
|
11154
|
-
const reach = Math.max(40, Math.min(dx * 0.6, 240));
|
|
11155
|
-
return `M${from.x},${from.y} C${from.x + reach},${from.y} ${to.x - reach},${to.y} ${to.x},${to.y}`;
|
|
11156
|
-
}
|
|
11157
|
-
function distanceToEdge(point, from, to, samples = 16) {
|
|
11158
|
-
const dx = Math.abs(to.x - from.x);
|
|
11159
|
-
const reach = Math.max(40, Math.min(dx * 0.6, 240));
|
|
11160
|
-
const c1 = { x: from.x + reach, y: from.y };
|
|
11161
|
-
const c2 = { x: to.x - reach, y: to.y };
|
|
11162
|
-
let best = Number.POSITIVE_INFINITY;
|
|
11163
|
-
for (let i = 0; i <= samples; i += 1) {
|
|
11164
|
-
const t = i / samples;
|
|
11165
|
-
const mt = 1 - t;
|
|
11166
|
-
const x = mt * mt * mt * from.x + 3 * mt * mt * t * c1.x + 3 * mt * t * t * c2.x + t * t * t * to.x;
|
|
11167
|
-
const y = mt * mt * mt * from.y + 3 * mt * mt * t * c1.y + 3 * mt * t * t * c2.y + t * t * t * to.y;
|
|
11168
|
-
best = Math.min(best, Math.hypot(x - point.x, y - point.y));
|
|
11169
|
-
}
|
|
11170
|
-
return best;
|
|
11171
|
-
}
|
|
11172
|
-
|
|
11173
10788
|
// src/edit/nodegraph/connect.ts
|
|
11174
10789
|
function canConnect(graph, from, to, rules = {}) {
|
|
11175
10790
|
if (from.nodeId === to.nodeId) {
|
package/dist/icons/index.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `@xenosystem/blocks` — rung 3 of the FRONT ladder — BLOCKS (`XENO FRONT-END HIERARCHY.md`).
|
|
3
3
|
* Families are subpaths: `@xenosystem/blocks/auth`, `/edit`, `/data`, `/time`, `/ops`, `/trust`,
|
|
4
|
-
* `/agent`. This root re-exports nothing on purpose: import the family you mount, so a product
|
|
4
|
+
* `/agent`, `/docs`, `/corpus`. This root re-exports nothing on purpose: import the family you mount, so a product
|
|
5
5
|
* carries only it. Naming rule: `XENO PACKAGE NAMING - STANDARD.md`.
|
|
6
6
|
*/
|
|
7
7
|
declare const BLOCKS_VERSION = "0.8.1";
|
package/dist/ops/index.js
CHANGED
|
@@ -7,10 +7,12 @@ import {
|
|
|
7
7
|
terminalThemeVar
|
|
8
8
|
} from "../chunk-WE6A2DTY.js";
|
|
9
9
|
import {
|
|
10
|
-
ICONS
|
|
10
|
+
ICONS
|
|
11
|
+
} from "../chunk-YZ6J6A67.js";
|
|
12
|
+
import {
|
|
11
13
|
PlusGlyph,
|
|
12
14
|
XGlyph
|
|
13
|
-
} from "../chunk-
|
|
15
|
+
} from "../chunk-YSMDUREC.js";
|
|
14
16
|
import "../chunk-2KG3PWR4.js";
|
|
15
17
|
|
|
16
18
|
// src/ops/console/panel.ts
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `XenoGraphScale` — the only thing in this package that converts between pixels and graph space.
|
|
3
|
+
*
|
|
4
|
+
* Same discipline as the timeline and curve scales, **copied with attribution rather than shared**:
|
|
5
|
+
* a graph is 2D with a *uniform* zoom (a node must not shear), where the timeline is 1D and the
|
|
6
|
+
* curve has two independent axes. One type covering all three would be three things wearing one
|
|
7
|
+
* name.
|
|
8
|
+
*
|
|
9
|
+
* Kept verbatim because it is right in all three: state is `(zoom, x, y)` rather than a pair of
|
|
10
|
+
* corners, every dimension is clamped at the single construction site so a collapsed container
|
|
11
|
+
* cannot poison it with a sticky NaN, and zoom-to-cursor derives its offset from the **clamped**
|
|
12
|
+
* zoom.
|
|
13
|
+
*
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
/** The visible window. */
|
|
17
|
+
interface XenoGraphScaleState {
|
|
18
|
+
/** Uniform zoom. A node must not shear, so there is one scalar and not two. */
|
|
19
|
+
zoom: number;
|
|
20
|
+
/** Graph-space coordinate at the viewport's top-left. */
|
|
21
|
+
x: number;
|
|
22
|
+
y: number;
|
|
23
|
+
}
|
|
24
|
+
/** Container geometry. */
|
|
25
|
+
interface XenoGraphScaleGeometry {
|
|
26
|
+
widthPx: number;
|
|
27
|
+
heightPx: number;
|
|
28
|
+
}
|
|
29
|
+
/** Zoom limits. */
|
|
30
|
+
interface XenoGraphZoom {
|
|
31
|
+
min: number;
|
|
32
|
+
max: number;
|
|
33
|
+
}
|
|
34
|
+
/** Default zoom range: 5% to 400%, the band every surveyed editor lands in. */
|
|
35
|
+
declare const DEFAULT_GRAPH_ZOOM: XenoGraphZoom;
|
|
36
|
+
/** An immutable graph→view mapping. */
|
|
37
|
+
declare class XenoGraphScale {
|
|
38
|
+
readonly zoom: number;
|
|
39
|
+
readonly x: number;
|
|
40
|
+
readonly y: number;
|
|
41
|
+
readonly widthPx: number;
|
|
42
|
+
readonly heightPx: number;
|
|
43
|
+
constructor(state: XenoGraphScaleState, geometry: XenoGraphScaleGeometry);
|
|
44
|
+
/** Graph-space width of the viewport. */
|
|
45
|
+
get graphWidth(): number;
|
|
46
|
+
/** Graph-space height of the viewport. */
|
|
47
|
+
get graphHeight(): number;
|
|
48
|
+
/** The visible rectangle in graph space. */
|
|
49
|
+
get viewport(): {
|
|
50
|
+
x: number;
|
|
51
|
+
y: number;
|
|
52
|
+
width: number;
|
|
53
|
+
height: number;
|
|
54
|
+
};
|
|
55
|
+
/** Container-relative screen point → graph point. */
|
|
56
|
+
screenToGraph(px: number, py: number): {
|
|
57
|
+
x: number;
|
|
58
|
+
y: number;
|
|
59
|
+
};
|
|
60
|
+
/** Graph point → container-relative screen point. */
|
|
61
|
+
graphToScreen(gx: number, gy: number): {
|
|
62
|
+
x: number;
|
|
63
|
+
y: number;
|
|
64
|
+
};
|
|
65
|
+
/** A graph-space distance → pixels. */
|
|
66
|
+
toPx(distance: number): number;
|
|
67
|
+
/** A pixel distance → graph space. Used for screen-space hit thresholds. */
|
|
68
|
+
toGraph(px: number): number;
|
|
69
|
+
/** A copy with new state. */
|
|
70
|
+
with(state: Partial<XenoGraphScaleState>): XenoGraphScale;
|
|
71
|
+
/** A copy with new geometry. */
|
|
72
|
+
resized(geometry: Partial<XenoGraphScaleGeometry>): XenoGraphScale;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Zoom about a cursor, keeping the graph point under the pointer fixed.
|
|
76
|
+
*
|
|
77
|
+
* ```
|
|
78
|
+
* anchor = screenToGraph(px, py) // BEFORE
|
|
79
|
+
* zoom' = clamp(zoom * factor) // clamp FIRST
|
|
80
|
+
* x' = anchor.x - px / zoom'
|
|
81
|
+
* ```
|
|
82
|
+
*
|
|
83
|
+
* 🔴 The offset is derived from the **clamped** zoom. Using the requested one moves the graph under
|
|
84
|
+
* a stationary cursor at both ends of the range — exactly when the user is zoomed in on the thing
|
|
85
|
+
* they care about.
|
|
86
|
+
*
|
|
87
|
+
* @param scale - Current scale.
|
|
88
|
+
* @param px - Container-relative pointer x.
|
|
89
|
+
* @param py - Container-relative pointer y.
|
|
90
|
+
* @param factor - Requested multiplier.
|
|
91
|
+
* @param bounds - Zoom limits.
|
|
92
|
+
* @returns New state.
|
|
93
|
+
*/
|
|
94
|
+
declare function zoomAboutCursor(scale: XenoGraphScale, px: number, py: number, factor: number, bounds?: XenoGraphZoom): XenoGraphScaleState;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* The scale that frames a graph-space rectangle.
|
|
98
|
+
*
|
|
99
|
+
* @param bounds - The rectangle to frame.
|
|
100
|
+
* @param widthPx - Viewport width.
|
|
101
|
+
* @param heightPx - Viewport height.
|
|
102
|
+
* @param bounds_ - Zoom limits.
|
|
103
|
+
* @param paddingPx - Screen-space padding.
|
|
104
|
+
* @returns The state that shows it.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```ts
|
|
108
|
+
* controller.fitTo(graphBounds(graph.nodes))
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
declare function fitToBounds(bounds: {
|
|
112
|
+
x: number;
|
|
113
|
+
y: number;
|
|
114
|
+
width: number;
|
|
115
|
+
height: number;
|
|
116
|
+
} | null, widthPx: number, heightPx: number, zoomBounds?: XenoGraphZoom, paddingPx?: number): XenoGraphScaleState;
|
|
117
|
+
/** The bounding box of a set of nodes, or `null` when there are none. */
|
|
118
|
+
declare function graphBounds(nodes: readonly {
|
|
119
|
+
x: number;
|
|
120
|
+
y: number;
|
|
121
|
+
width: number;
|
|
122
|
+
height: number;
|
|
123
|
+
}[]): {
|
|
124
|
+
x: number;
|
|
125
|
+
y: number;
|
|
126
|
+
width: number;
|
|
127
|
+
height: number;
|
|
128
|
+
} | null;
|
|
129
|
+
|
|
130
|
+
export { DEFAULT_GRAPH_ZOOM as D, type XenoGraphScaleState as X, type XenoGraphZoom as a, XenoGraphScale as b, type XenoGraphScaleGeometry as c, fitToBounds as f, graphBounds as g, zoomAboutCursor as z };
|
package/dist/time/index.js
CHANGED