vortx-gl 1.0.29 → 1.0.31

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,5 +1,6 @@
1
1
  import { GraphEngine } from '../engine/renderer';
2
2
  import { NodeInput, EdgeInput, Stylesheet, RadialMenuItem } from '../engine/types';
3
+ import { PartialGraphConfig } from '../engine/config';
3
4
  interface GraphData {
4
5
  nodes: Array<{
5
6
  data: NodeInput;
@@ -17,6 +18,10 @@ type __VLS_Props = {
17
18
  theme?: "dark" | "light";
18
19
  showUI?: boolean;
19
20
  autoFit?: boolean;
21
+ /** Overrides for any subset of GraphConfig (zoom/performance/interaction/
22
+ * style/graphOptions thresholds) — see engine/config.ts. Applied once, at
23
+ * engine construction; changing it after mount has no effect. */
24
+ config?: PartialGraphConfig;
20
25
  };
21
26
  declare const _default: import('vue').DefineComponent<__VLS_Props, {
22
27
  engine: import('vue').ShallowRef<GraphEngine | null, GraphEngine | null>;
@@ -5,16 +5,30 @@ import { EdgeStore } from './EdgeStore';
5
5
  import { ComboStore } from './ComboStore';
6
6
  import { Camera } from './Camera';
7
7
  import { GpuBufferManager } from '../gpu/GpuBufferManager';
8
+ import { SpatialIndex } from '../spatialIndex';
8
9
  export interface EngineContext {
9
10
  gl: WebGL2RenderingContext;
10
11
  canvas: HTMLCanvasElement;
11
12
  emitter: EventEmitter;
12
13
  requestRender: () => void;
14
+ /**
15
+ * Mark the overlay's projected-position cache stale — for mutations that move
16
+ * a node or change which nodes are hidden. Metadata-only updates must NOT
17
+ * call it: the projection pass is O(N). See GraphEngine.invalidateProjection.
18
+ */
19
+ invalidateProjection: () => void;
20
+ /**
21
+ * As invalidateProjection(), plus the caches keyed by node *index*. Node
22
+ * add/remove only — removal compacts the position array (swap-with-last), so
23
+ * an index-keyed cache then describes the wrong node.
24
+ */
25
+ invalidateNodeSet: () => void;
13
26
  nodes: NodeStore;
14
27
  edges: EdgeStore;
15
28
  combos: ComboStore;
16
29
  camera: Camera;
17
30
  gpu: GpuBufferManager;
31
+ spatialIndex: SpatialIndex;
18
32
  state: InteractionState;
19
33
  theme: "dark" | "light";
20
34
  }
@@ -3,9 +3,6 @@ export declare class InteractionManager {
3
3
  private lastMouse;
4
4
  private isPanning;
5
5
  private isDraggingCombo;
6
- private _managedComboDown;
7
- private _managedComboMoved;
8
- private _managedComboDownPos;
9
6
  private engine;
10
7
  private canvas;
11
8
  private groups;
@@ -65,6 +65,22 @@ export declare class GraphEngine {
65
65
  emitEvent<T = unknown>(event: string, data?: T): void;
66
66
  requestRender(): void;
67
67
  private startRenderLoop;
68
+ /**
69
+ * Mark the overlay's projected-position cache stale. OverlayManager projects
70
+ * world → screen once per frame and reuses that cache unless the camera or
71
+ * the positions changed, so anything that moves a node or changes which nodes
72
+ * are hidden must say so — otherwise the overlay keeps drawing the previous
73
+ * geometry (labels, icons, badges, hulls) until the next pan/zoom.
74
+ */
75
+ invalidateProjection(): void;
76
+ /**
77
+ * As invalidateProjection(), plus the adaptive-LOD viewport count, which is
78
+ * keyed by node index and so is wrong (not just stale) once a removal
79
+ * compacts the position array. Both are recomputed lazily by the next frame.
80
+ * The spatial index is kept coherent incrementally by the stores — a full
81
+ * rebuild is O(N) over a string-keyed Map, never run it per mutation.
82
+ */
83
+ invalidateNodeSet(): void;
68
84
  render(): void;
69
85
  /**
70
86
  * Capture the current graph view as an image data URL (PNG by default).
@@ -556,10 +556,23 @@ export interface GraphEventPayloadMap {
556
556
  "node:drag": {
557
557
  id: string | number;
558
558
  index: number;
559
- position: {
559
+ x: number;
560
+ y: number;
561
+ };
562
+ /**
563
+ * Batched counterpart of "node:drag" for a multi-select rigid drag: every
564
+ * node that moved this tick, not just the primary one "node:drag" reports.
565
+ * "node:drag" stays single-emit for perf (one dispatch, not N, per
566
+ * mousemove) — this fires alongside it, only when more than one node
567
+ * actually moved, for listeners that need the whole batch (e.g. undo/redo
568
+ * or persistence tracking "what moved", not a live per-frame visual).
569
+ */
570
+ "nodes:drag": {
571
+ moves: {
572
+ id: string | number;
560
573
  x: number;
561
574
  y: number;
562
- };
575
+ }[];
563
576
  };
564
577
  "edge:add": {
565
578
  id: string;
@@ -603,6 +616,10 @@ export interface GraphEventPayloadMap {
603
616
  id: string;
604
617
  additive?: boolean;
605
618
  };
619
+ /** Edges caught by a box selection (mirrors "nodes:select" for the edge side). */
620
+ "edges:select": {
621
+ ids: string[];
622
+ };
606
623
  "combo:select": {
607
624
  id: string;
608
625
  };
@@ -662,10 +679,8 @@ export interface GraphEventPayloadMap {
662
679
  zoom: number;
663
680
  };
664
681
  "boxselect": {
665
- x: number;
666
- y: number;
667
- width: number;
668
- height: number;
682
+ indices: number[];
683
+ ids: (string | number)[];
669
684
  };
670
685
  "boxend": void;
671
686
  }
package/dist/index.d.ts CHANGED
@@ -1,12 +1,11 @@
1
1
  import { NodeInput, EdgeInput } from './engine/types';
2
- export declare const VORTX_BUILD = "GROUP-FIX-4";
3
2
  export { GraphEngine } from './engine/renderer';
4
3
  export * from './engine/types';
5
4
  export { default as GraphCanvas } from './components/GraphCanvas';
6
5
  export * from './engine/api';
7
6
  export { Minimap } from './engine/Minimap';
8
7
  export { GraphConfig, resolveConfig } from './engine/config';
9
- export type { GraphConfigType, PartialGraphConfig, ResolvedGraphConfig, GraphOptions } from './engine/config';
8
+ export type { GraphConfigType, PartialGraphConfig, ResolvedGraphConfig, GraphOptions, } from './engine/config';
10
9
  export { resolveNodeStyle, resolveNodeSelectStyle, resolveEdgeStyle, resolveEdgeSelectStyle, resolveEdgeClass, resolveIconKey, } from './engine/utils/styleResolver';
11
10
  export type { ResolvedNodeStyle, ResolvedNodeSelectStyle, ResolvedEdgeStyle, ResolvedEdgeSelectStyle, EdgeStyleClass, NodeElement, EdgeElement, } from './engine/utils/styleResolver';
12
11
  export declare const createGraphData: (nodes: NodeInput[], edges: EdgeInput[]) => {
package/dist/vortx-gl.css CHANGED
@@ -874,7 +874,7 @@ input[data-v-bf144eba] {
874
874
  transition: width 0.3s ease, background-color 0.3s ease;
875
875
  }
876
876
 
877
- .vortx-gl-container[data-v-ecc29c67] {
877
+ .vortx-gl-container[data-v-6ba1469f] {
878
878
  position: relative;
879
879
  width: 100%;
880
880
  height: 100%;
@@ -886,13 +886,13 @@ input[data-v-bf144eba] {
886
886
  }
887
887
 
888
888
  /* Top Right Controls */
889
- .top-right-controls-container[data-v-ecc29c67] {
889
+ .top-right-controls-container[data-v-6ba1469f] {
890
890
  position: absolute;
891
891
  top: var(--vortx-space-md);
892
892
  right: var(--vortx-space-md);
893
893
  z-index: 1100;
894
894
  }
895
- .bottom-right-controls-container[data-v-ecc29c67] {
895
+ .bottom-right-controls-container[data-v-6ba1469f] {
896
896
  position: absolute;
897
897
  bottom: var(--vortx-space-md);
898
898
  right: var(--vortx-space-md);
@@ -902,21 +902,21 @@ input[data-v-bf144eba] {
902
902
  gap: var(--vortx-space-sm);
903
903
  z-index: 1100;
904
904
  }
905
- .bottom-right-controls-container[data-v-ecc29c67] .top-right-controls {
905
+ .bottom-right-controls-container[data-v-6ba1469f] .top-right-controls {
906
906
  position: static;
907
907
  transform: none;
908
908
  }
909
- .controls-stack[data-v-ecc29c67] {
909
+ .controls-stack[data-v-6ba1469f] {
910
910
  display: flex;
911
911
  gap: var(--vortx-space-sm);
912
912
  align-items: flex-start;
913
913
  }
914
- .controls-stack[data-v-ecc29c67] .top-right-controls {
914
+ .controls-stack[data-v-6ba1469f] .top-right-controls {
915
915
  position: static;
916
916
  transform: none;
917
917
  }
918
- .main-canvas[data-v-ecc29c67],
919
- .overlay-canvas[data-v-ecc29c67] {
918
+ .main-canvas[data-v-6ba1469f],
919
+ .overlay-canvas[data-v-6ba1469f] {
920
920
  position: absolute;
921
921
  top: 0;
922
922
  left: 0;
@@ -924,12 +924,12 @@ input[data-v-bf144eba] {
924
924
  height: 100%;
925
925
  display: block;
926
926
  }
927
- .overlay-canvas[data-v-ecc29c67] {
927
+ .overlay-canvas[data-v-6ba1469f] {
928
928
  pointer-events: none;
929
929
  }
930
930
 
931
931
  /* Mobile Menu */
932
- .mobile-menu[data-v-ecc29c67] {
932
+ .mobile-menu[data-v-6ba1469f] {
933
933
  position: fixed;
934
934
  bottom: var(--vortx-space-md);
935
935
  left: 50%;
@@ -941,7 +941,7 @@ input[data-v-bf144eba] {
941
941
  z-index: 1000;
942
942
  gap: 4px;
943
943
  }
944
- .menu-btn[data-v-ecc29c67] {
944
+ .menu-btn[data-v-6ba1469f] {
945
945
  background: transparent;
946
946
  border: none;
947
947
  color: var(--vortx-text-dim);
@@ -955,18 +955,18 @@ input[data-v-bf144eba] {
955
955
  transition: all 0.2s;
956
956
  min-width: 70px;
957
957
  }
958
- .menu-btn span[data-v-ecc29c67] {
958
+ .menu-btn span[data-v-6ba1469f] {
959
959
  font-size: 10px;
960
960
  font-weight: 600;
961
961
  text-transform: uppercase;
962
962
  letter-spacing: 0.5px;
963
963
  }
964
- .menu-btn.active[data-v-ecc29c67] {
964
+ .menu-btn.active[data-v-6ba1469f] {
965
965
  background: var(--vortx-primary);
966
966
  color: white;
967
967
  box-shadow: 0 4px 12px var(--vortx-primary-glow);
968
968
  }
969
- .menu-divider[data-v-ecc29c67] {
969
+ .menu-divider[data-v-6ba1469f] {
970
970
  width: 1px;
971
971
  height: 24px;
972
972
  background: var(--vortx-border);
@@ -974,14 +974,14 @@ input[data-v-bf144eba] {
974
974
  }
975
975
 
976
976
  /* Mobile Drawers */
977
- .drawer-overlay[data-v-ecc29c67] {
977
+ .drawer-overlay[data-v-6ba1469f] {
978
978
  position: fixed;
979
979
  inset: 0;
980
980
  background: rgba(0, 0, 0, 0.4);
981
981
  backdrop-filter: blur(4px);
982
982
  z-index: 1300;
983
983
  }
984
- .mobile-drawer[data-v-ecc29c67] {
984
+ .mobile-drawer[data-v-6ba1469f] {
985
985
  position: absolute;
986
986
  bottom: 0;
987
987
  left: 0;
@@ -991,37 +991,37 @@ input[data-v-bf144eba] {
991
991
  padding-bottom: env(safe-area-inset-bottom, var(--vortx-space-lg));
992
992
  animation: vortxSlideUp 0.3s cubic-bezier(0.16, 1, 0.3, 1);
993
993
  }
994
- .drawer-handle[data-v-ecc29c67] {
994
+ .drawer-handle[data-v-6ba1469f] {
995
995
  width: 40px;
996
996
  height: 4px;
997
997
  background: var(--vortx-border);
998
998
  border-radius: 2px;
999
999
  margin: 0 auto var(--vortx-space-md);
1000
1000
  }
1001
- .drawer-header[data-v-ecc29c67] {
1001
+ .drawer-header[data-v-6ba1469f] {
1002
1002
  display: flex;
1003
1003
  justify-content: space-between;
1004
1004
  align-items: center;
1005
1005
  margin-bottom: var(--vortx-space-md);
1006
1006
  }
1007
- .drawer-header h3[data-v-ecc29c67] {
1007
+ .drawer-header h3[data-v-6ba1469f] {
1008
1008
  margin: 0;
1009
1009
  font-size: 16px;
1010
1010
  font-weight: 700;
1011
1011
  }
1012
- .drawer-close[data-v-ecc29c67] {
1012
+ .drawer-close[data-v-6ba1469f] {
1013
1013
  background: transparent;
1014
1014
  border: none;
1015
1015
  color: var(--vortx-text-dim);
1016
1016
  cursor: pointer;
1017
1017
  }
1018
- .drawer-content[data-v-ecc29c67] {
1018
+ .drawer-content[data-v-6ba1469f] {
1019
1019
  padding: var(--vortx-space-sm) 0;
1020
1020
  }
1021
1021
 
1022
1022
  /* Fix desktop components inside drawer */
1023
- .drawer-content[data-v-ecc29c67] .layout-controls,
1024
- .drawer-content[data-v-ecc29c67] .top-right-controls {
1023
+ .drawer-content[data-v-6ba1469f] .layout-controls,
1024
+ .drawer-content[data-v-6ba1469f] .top-right-controls {
1025
1025
  position: static;
1026
1026
  transform: none;
1027
1027
  width: 100%;
@@ -1034,12 +1034,12 @@ input[data-v-bf144eba] {
1034
1034
  flex-wrap: wrap;
1035
1035
  justify-content: center;
1036
1036
  }
1037
- .drawer-content[data-v-ecc29c67] .divider {
1037
+ .drawer-content[data-v-6ba1469f] .divider {
1038
1038
  display: none;
1039
1039
  }
1040
1040
 
1041
1041
  /* Help Trigger */
1042
- .help-trigger[data-v-ecc29c67] {
1042
+ .help-trigger[data-v-6ba1469f] {
1043
1043
  position: absolute;
1044
1044
  bottom: var(--vortx-space-md);
1045
1045
  right: var(--vortx-space-md);
@@ -1059,7 +1059,7 @@ input[data-v-bf144eba] {
1059
1059
  transition: all 0.2s;
1060
1060
  color: var(--vortx-text-dim);
1061
1061
  }
1062
- .help-trigger[data-v-ecc29c67]:hover {
1062
+ .help-trigger[data-v-6ba1469f]:hover {
1063
1063
  background: var(--vortx-primary);
1064
1064
  color: white;
1065
1065
  border-color: white;
@@ -1067,7 +1067,7 @@ input[data-v-bf144eba] {
1067
1067
  }
1068
1068
 
1069
1069
  /* Help Modal */
1070
- .help-modal-overlay[data-v-ecc29c67] {
1070
+ .help-modal-overlay[data-v-6ba1469f] {
1071
1071
  position: fixed;
1072
1072
  inset: 0;
1073
1073
  background: rgba(0, 0, 0, 0.4);
@@ -1077,45 +1077,45 @@ input[data-v-bf144eba] {
1077
1077
  align-items: center;
1078
1078
  justify-content: center;
1079
1079
  }
1080
- .help-modal[data-v-ecc29c67] {
1080
+ .help-modal[data-v-6ba1469f] {
1081
1081
  width: 340px;
1082
1082
  border-radius: var(--vortx-radius-lg);
1083
1083
  overflow: hidden;
1084
1084
  animation: vortxScaleIn 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
1085
1085
  }
1086
- .help-header[data-v-ecc29c67] {
1086
+ .help-header[data-v-6ba1469f] {
1087
1087
  padding: var(--vortx-space-md);
1088
1088
  border-bottom: 1px solid var(--vortx-border);
1089
1089
  display: flex;
1090
1090
  justify-content: space-between;
1091
1091
  align-items: center;
1092
1092
  }
1093
- .help-header h3[data-v-ecc29c67] {
1093
+ .help-header h3[data-v-6ba1469f] {
1094
1094
  margin: 0;
1095
1095
  font-size: 16px;
1096
1096
  }
1097
- .shortcut-row[data-v-ecc29c67] {
1097
+ .shortcut-row[data-v-6ba1469f] {
1098
1098
  display: flex;
1099
1099
  justify-content: space-between;
1100
1100
  align-items: center;
1101
1101
  padding: 12px var(--vortx-space-md);
1102
1102
  border-bottom: 1px solid var(--vortx-border);
1103
1103
  }
1104
- .shortcut-row[data-v-ecc29c67]:last-child {
1104
+ .shortcut-row[data-v-6ba1469f]:last-child {
1105
1105
  border-bottom: none;
1106
1106
  }
1107
- .key-badge[data-v-ecc29c67] {
1107
+ .key-badge[data-v-6ba1469f] {
1108
1108
  background: var(--vortx-border);
1109
1109
  padding: 4px 10px;
1110
1110
  border-radius: var(--vortx-radius-sm);
1111
1111
  font-family: monospace;
1112
1112
  font-size: 12px;
1113
1113
  }
1114
- .action-desc[data-v-ecc29c67] {
1114
+ .action-desc[data-v-6ba1469f] {
1115
1115
  font-size: 13px;
1116
1116
  color: var(--vortx-text-dim);
1117
1117
  }
1118
- .close-btn[data-v-ecc29c67] {
1118
+ .close-btn[data-v-6ba1469f] {
1119
1119
  background: transparent;
1120
1120
  border: none;
1121
1121
  color: var(--vortx-text-dim);
@@ -1123,13 +1123,13 @@ input[data-v-bf144eba] {
1123
1123
  cursor: pointer;
1124
1124
  }
1125
1125
  @media (max-width: 768px) {
1126
- .help-trigger[data-v-ecc29c67] {
1126
+ .help-trigger[data-v-6ba1469f] {
1127
1127
  display: none;
1128
1128
  }
1129
1129
  }
1130
1130
 
1131
1131
  /* Connecting Toast */
1132
- .connecting-toast[data-v-ecc29c67] {
1132
+ .connecting-toast[data-v-6ba1469f] {
1133
1133
  position: fixed;
1134
1134
  top: var(--vortx-space-lg);
1135
1135
  left: 50%;
@@ -1140,20 +1140,20 @@ input[data-v-bf144eba] {
1140
1140
  display: flex;
1141
1141
  align-items: center;
1142
1142
  gap: 16px;
1143
- animation: vortxSlideDown-ecc29c67 0.3s cubic-bezier(0.16, 1, 0.3, 1);
1143
+ animation: vortxSlideDown-6ba1469f 0.3s cubic-bezier(0.16, 1, 0.3, 1);
1144
1144
  }
1145
- .toast-content[data-v-ecc29c67] {
1145
+ .toast-content[data-v-6ba1469f] {
1146
1146
  display: flex;
1147
1147
  align-items: center;
1148
1148
  gap: 16px;
1149
1149
  flex: 1;
1150
1150
  }
1151
- .edge-config-group[data-v-ecc29c67] {
1151
+ .edge-config-group[data-v-6ba1469f] {
1152
1152
  display: flex;
1153
1153
  align-items: center;
1154
1154
  gap: 12px;
1155
1155
  }
1156
- .vortx-input-sm[data-v-ecc29c67] {
1156
+ .vortx-input-sm[data-v-6ba1469f] {
1157
1157
  background: rgba(255, 255, 255, 0.05);
1158
1158
  border: 1px solid var(--vortx-border);
1159
1159
  border-radius: var(--vortx-radius-sm);
@@ -1163,24 +1163,24 @@ input[data-v-bf144eba] {
1163
1163
  width: 140px;
1164
1164
  height: 32px;
1165
1165
  }
1166
- .vortx-input-sm.narrow[data-v-ecc29c67] {
1166
+ .vortx-input-sm.narrow[data-v-6ba1469f] {
1167
1167
  width: 60px;
1168
1168
  }
1169
- .color-presets[data-v-ecc29c67] {
1169
+ .color-presets[data-v-6ba1469f] {
1170
1170
  display: flex;
1171
1171
  gap: 6px;
1172
1172
  }
1173
- .color-dot[data-v-ecc29c67] {
1173
+ .color-dot[data-v-6ba1469f] {
1174
1174
  width: 18px;
1175
1175
  height: 18px;
1176
1176
  border-radius: 50%;
1177
1177
  cursor: pointer;
1178
1178
  transition: transform 0.2s;
1179
1179
  }
1180
- .color-dot[data-v-ecc29c67]:hover {
1180
+ .color-dot[data-v-6ba1469f]:hover {
1181
1181
  transform: scale(1.2);
1182
1182
  }
1183
- @keyframes vortxPulse-ecc29c67 {
1183
+ @keyframes vortxPulse-6ba1469f {
1184
1184
  0% {
1185
1185
  transform: scale(1);
1186
1186
  opacity: 1;
@@ -1194,7 +1194,7 @@ input[data-v-bf144eba] {
1194
1194
  opacity: 1;
1195
1195
  }
1196
1196
  }
1197
- @keyframes vortxSlideDown-ecc29c67 {
1197
+ @keyframes vortxSlideDown-6ba1469f {
1198
1198
  from {
1199
1199
  transform: translate(-50%, -20px);
1200
1200
  opacity: 0;