@xenosystem/blocks 0.21.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.
@@ -1,9 +1,31 @@
1
1
  import {
2
- CollapseGlyph,
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-6ZIWULSK.js";
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) {
@@ -1,8 +1,10 @@
1
1
  import {
2
2
  ICONS,
3
- ICON_UNITS,
3
+ ICON_UNITS
4
+ } from "../chunk-YZ6J6A67.js";
5
+ import {
4
6
  Svg
5
- } from "../chunk-6ZIWULSK.js";
7
+ } from "../chunk-YSMDUREC.js";
6
8
  import "../chunk-2KG3PWR4.js";
7
9
  export {
8
10
  ICONS,
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-6ZIWULSK.js";
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 };
@@ -13,7 +13,8 @@ import {
13
13
  StopIcon,
14
14
  TRANSPORT_ICONS,
15
15
  TRANSPORT_RUNTIME_UNITS
16
- } from "../chunk-6ZIWULSK.js";
16
+ } from "../chunk-YZ6J6A67.js";
17
+ import "../chunk-YSMDUREC.js";
17
18
  import {
18
19
  __reExport
19
20
  } from "../chunk-2KG3PWR4.js";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@xenosystem/blocks",
3
- "version": "0.21.0",
4
- "description": "XENO Blocks — rung 3 of the front ladder: components composed into a contract-bearing unit (manifest: ports, intents, commands, agent surface), rendered by @xenosystem/workbench. ONE package, one subpath per family (/auth, /edit, /data, /time, /ops, /trust, /agent, /docs; framework-free /docs/core and pure /docs/format), one named export per block — XENO PACKAGE NAMING - STANDARD.md.",
3
+ "version": "0.23.0",
4
+ "description": "XENO Blocks — rung 3 of the front ladder: components composed into a contract-bearing unit (manifest: ports, intents, commands, agent surface), rendered by @xenosystem/workbench. ONE package, one subpath per family (/auth, /edit, /data, /time, /ops, /trust, /agent, /docs, /corpus; framework-free /docs/core and pure /docs/format), one named export per block — XENO PACKAGE NAMING - STANDARD.md.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
7
7
  "sideEffects": false,
@@ -61,6 +61,10 @@
61
61
  "./docs/format": {
62
62
  "types": "./dist/docs/format.d.ts",
63
63
  "import": "./dist/docs/format.js"
64
+ },
65
+ "./corpus": {
66
+ "types": "./dist/corpus/index.d.ts",
67
+ "import": "./dist/corpus/index.js"
64
68
  }
65
69
  },
66
70
  "files": [