@ltm-blueverse/alpha-semantic-hub 0.7.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.
Files changed (146) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1674 -0
  3. package/explorer/README.md +282 -0
  4. package/explorer/src/App.css +0 -0
  5. package/explorer/src/App.tsx +2035 -0
  6. package/explorer/src/ErrorBoundary.tsx +112 -0
  7. package/explorer/src/ExploreWorkspaceTabs.tsx +29 -0
  8. package/explorer/src/assets/hero.png +0 -0
  9. package/explorer/src/assets/react.svg +1 -0
  10. package/explorer/src/assets/vite.svg +1 -0
  11. package/explorer/src/explorerCapabilities.ts +24 -0
  12. package/explorer/src/index.css +71 -0
  13. package/explorer/src/main.tsx +7 -0
  14. package/explorer/src/store/edgePairKeys.d.ts +2 -0
  15. package/explorer/src/store/edgePairKeys.js +7 -0
  16. package/explorer/src/store/graphStore.ts +183 -0
  17. package/explorer/src/store/registryStore.ts +78 -0
  18. package/explorer/src/types.d.ts +16 -0
  19. package/explorer/src/ui/primitives.css +414 -0
  20. package/explorer/src/ui/primitives.tsx +170 -0
  21. package/explorer/src/workspaces/DecisionWorkspace/DecisionWorkspace.tsx +279 -0
  22. package/explorer/src/workspaces/DiffMergeWorkspace/DiffMergeWorkspace.tsx +121 -0
  23. package/explorer/src/workspaces/EnrichWorkspace/EntityResolutionTab.tsx +454 -0
  24. package/explorer/src/workspaces/EnrichWorkspace/RegistryTab.tsx +285 -0
  25. package/explorer/src/workspaces/GraphWorkspace/GraphCanvas.tsx +2673 -0
  26. package/explorer/src/workspaces/GraphWorkspace/GraphInspectorPanel.tsx +771 -0
  27. package/explorer/src/workspaces/GraphWorkspace/GraphLoadingOverlay.tsx +410 -0
  28. package/explorer/src/workspaces/GraphWorkspace/GraphWorkspace.tsx +3584 -0
  29. package/explorer/src/workspaces/GraphWorkspace/MarkdownContentViewer.tsx +705 -0
  30. package/explorer/src/workspaces/GraphWorkspace/SigmaSceneAdapter.tsx +55 -0
  31. package/explorer/src/workspaces/GraphWorkspace/TimelinePanel.tsx +193 -0
  32. package/explorer/src/workspaces/GraphWorkspace/behaviors/clickSelectionBehavior.ts +29 -0
  33. package/explorer/src/workspaces/GraphWorkspace/behaviors/fitViewBehavior.ts +15 -0
  34. package/explorer/src/workspaces/GraphWorkspace/behaviors/focusCameraBehavior.ts +25 -0
  35. package/explorer/src/workspaces/GraphWorkspace/behaviors/hoverActivationBehavior.ts +15 -0
  36. package/explorer/src/workspaces/GraphWorkspace/behaviors/pathHighlightBehavior.ts +52 -0
  37. package/explorer/src/workspaces/GraphWorkspace/behaviors/searchFocusBehavior.ts +36 -0
  38. package/explorer/src/workspaces/GraphWorkspace/behaviors/types.ts +41 -0
  39. package/explorer/src/workspaces/GraphWorkspace/behaviors/viewModeSwitchBehavior.ts +29 -0
  40. package/explorer/src/workspaces/GraphWorkspace/graphAnalytics.ts +779 -0
  41. package/explorer/src/workspaces/GraphWorkspace/graphColorLegend.ts +36 -0
  42. package/explorer/src/workspaces/GraphWorkspace/graphConfig.ts +102 -0
  43. package/explorer/src/workspaces/GraphWorkspace/graphEntityShape.ts +34 -0
  44. package/explorer/src/workspaces/GraphWorkspace/graphLoading.ts +84 -0
  45. package/explorer/src/workspaces/GraphWorkspace/graphSceneLayers.ts +775 -0
  46. package/explorer/src/workspaces/GraphWorkspace/graphSceneState.ts +2753 -0
  47. package/explorer/src/workspaces/GraphWorkspace/graphStructureLayer.ts +335 -0
  48. package/explorer/src/workspaces/GraphWorkspace/graphTheme.ts +1078 -0
  49. package/explorer/src/workspaces/GraphWorkspace/graphViewCopy.ts +41 -0
  50. package/explorer/src/workspaces/GraphWorkspace/localGraphTransition.ts +9 -0
  51. package/explorer/src/workspaces/GraphWorkspace/markdownEditorState.ts +114 -0
  52. package/explorer/src/workspaces/GraphWorkspace/markdownResourceClient.ts +103 -0
  53. package/explorer/src/workspaces/GraphWorkspace/markdownUrlSafety.ts +29 -0
  54. package/explorer/src/workspaces/GraphWorkspace/nodeMarkdownSync.ts +81 -0
  55. package/explorer/src/workspaces/GraphWorkspace/pluginRegistryPredicates.ts +24 -0
  56. package/explorer/src/workspaces/GraphWorkspace/plugins/explorationEffectsPlugin.tsx +353 -0
  57. package/explorer/src/workspaces/GraphWorkspace/plugins/explorationEffectsPluginPhaseC.tsx +522 -0
  58. package/explorer/src/workspaces/GraphWorkspace/plugins/index.ts +16 -0
  59. package/explorer/src/workspaces/GraphWorkspace/plugins/legendPlugin.tsx +120 -0
  60. package/explorer/src/workspaces/GraphWorkspace/plugins/neighborhoodPanelPlugin.tsx +231 -0
  61. package/explorer/src/workspaces/GraphWorkspace/plugins/temporalOverlayPlugin.tsx +142 -0
  62. package/explorer/src/workspaces/GraphWorkspace/plugins/types.ts +106 -0
  63. package/explorer/src/workspaces/GraphWorkspace/realtimeGraphAttributes.ts +50 -0
  64. package/explorer/src/workspaces/GraphWorkspace/scene.ts +79 -0
  65. package/explorer/src/workspaces/GraphWorkspace/sigmaNativeRendering.ts +421 -0
  66. package/explorer/src/workspaces/GraphWorkspace/smallGraphLayout.ts +135 -0
  67. package/explorer/src/workspaces/GraphWorkspace/temporalLifecyclePredicates.ts +31 -0
  68. package/explorer/src/workspaces/GraphWorkspace/temporalScrubberBounds.ts +46 -0
  69. package/explorer/src/workspaces/GraphWorkspace/temporalSnapshotGuards.ts +113 -0
  70. package/explorer/src/workspaces/GraphWorkspace/types.ts +395 -0
  71. package/explorer/src/workspaces/GraphWorkspace/useLoadGraph.ts +746 -0
  72. package/explorer/src/workspaces/GraphWorkspace/useMarkdownEditor.ts +177 -0
  73. package/explorer/src/workspaces/ImportExportWorkspace/ImportExportWorkspace.tsx +196 -0
  74. package/explorer/src/workspaces/LineageWorkspace/LineageDiagram.tsx +177 -0
  75. package/explorer/src/workspaces/ManageWorkspace/KGOverviewTab.tsx +291 -0
  76. package/explorer/src/workspaces/ManageWorkspace/OntologySummaryTab.tsx +346 -0
  77. package/explorer/src/workspaces/MemoryWorkspace.tsx +464 -0
  78. package/explorer/src/workspaces/OntologyWorkspace/AlignmentsTab.tsx +437 -0
  79. package/explorer/src/workspaces/OntologyWorkspace/HealthTab.tsx +214 -0
  80. package/explorer/src/workspaces/OntologyWorkspace/OntologyEditor.tsx +770 -0
  81. package/explorer/src/workspaces/OntologyWorkspace/OntologyLoader.tsx +913 -0
  82. package/explorer/src/workspaces/OntologyWorkspace/OntologyManager.tsx +930 -0
  83. package/explorer/src/workspaces/OntologyWorkspace/OntologySearch.tsx +590 -0
  84. package/explorer/src/workspaces/OntologyWorkspace/ProposalReview.tsx +429 -0
  85. package/explorer/src/workspaces/OntologyWorkspace/SKOSVocabularyManager.tsx +696 -0
  86. package/explorer/src/workspaces/OntologyWorkspace/ShaclStudio.tsx +363 -0
  87. package/explorer/src/workspaces/OntologyWorkspace/VersionsTab.tsx +546 -0
  88. package/explorer/src/workspaces/OntologyWorkspace/api.ts +164 -0
  89. package/explorer/src/workspaces/OntologyWorkspace/index.tsx +101 -0
  90. package/explorer/src/workspaces/OntologyWorkspace/ontologyEditorModel.ts +87 -0
  91. package/explorer/src/workspaces/OntologyWorkspace/ontologyUrlState.ts +94 -0
  92. package/explorer/src/workspaces/OntologyWorkspace/types.ts +119 -0
  93. package/explorer/src/workspaces/ReasoningWorkspace.tsx +243 -0
  94. package/explorer/src/workspaces/SparqlWorkspace/SparqlWorkspace.tsx +271 -0
  95. package/explorer/src/workspaces/VocabularyWorkspace/ConceptTree.tsx +97 -0
  96. package/explorer/src/workspaces/VocabularyWorkspace/ImportDropzone.tsx +95 -0
  97. package/explorer/src/workspaces/VocabularyWorkspace/PropertyPanel.tsx +116 -0
  98. package/explorer/src/workspaces/VocabularyWorkspace/Sidebar.tsx +112 -0
  99. package/explorer/src/workspaces/VocabularyWorkspace/VocabularyWorkspace.tsx +70 -0
  100. package/explorer/src/workspaces/VocabularyWorkspace/queries.ts +52 -0
  101. package/explorer/src/workspaces/VocabularyWorkspace/types.ts +24 -0
  102. package/package.json +31 -0
  103. package/plugins/.claude-plugin/README.md +262 -0
  104. package/plugins/.claude-plugin/marketplace.json +20 -0
  105. package/plugins/.claude-plugin/plugin.json +33 -0
  106. package/plugins/.cline-plugin/README.md +32 -0
  107. package/plugins/.cline-plugin/marketplace.json +17 -0
  108. package/plugins/.cline-plugin/plugin.json +35 -0
  109. package/plugins/.codex-plugin/marketplace.json +21 -0
  110. package/plugins/.codex-plugin/plugin.json +35 -0
  111. package/plugins/.continue-plugin/README.md +34 -0
  112. package/plugins/.continue-plugin/marketplace.json +17 -0
  113. package/plugins/.continue-plugin/plugin.json +33 -0
  114. package/plugins/.cursor-plugin/marketplace.json +18 -0
  115. package/plugins/.cursor-plugin/plugin.json +32 -0
  116. package/plugins/.openclaw-plugin/README.md +62 -0
  117. package/plugins/.openclaw-plugin/marketplace.json +18 -0
  118. package/plugins/.openclaw-plugin/plugin.json +46 -0
  119. package/plugins/.pi-plugin/README.md +35 -0
  120. package/plugins/.vscode-plugin/README.md +48 -0
  121. package/plugins/.vscode-plugin/marketplace.json +17 -0
  122. package/plugins/.vscode-plugin/plugin.json +35 -0
  123. package/plugins/.windsurf-plugin/README.md +39 -0
  124. package/plugins/.windsurf-plugin/marketplace.json +17 -0
  125. package/plugins/.windsurf-plugin/plugin.json +35 -0
  126. package/plugins/agents/decision-advisor.md +126 -0
  127. package/plugins/agents/explainability.md +129 -0
  128. package/plugins/agents/kg-assistant.md +73 -0
  129. package/plugins/hooks/hooks.json +12 -0
  130. package/plugins/skills/causal/SKILL.md +74 -0
  131. package/plugins/skills/change/SKILL.md +80 -0
  132. package/plugins/skills/decision/SKILL.md +197 -0
  133. package/plugins/skills/deduplicate/SKILL.md +41 -0
  134. package/plugins/skills/embed/SKILL.md +230 -0
  135. package/plugins/skills/explain/SKILL.md +44 -0
  136. package/plugins/skills/export/SKILL.md +67 -0
  137. package/plugins/skills/extract/SKILL.md +93 -0
  138. package/plugins/skills/ingest/SKILL.md +38 -0
  139. package/plugins/skills/ontology/SKILL.md +89 -0
  140. package/plugins/skills/policy/SKILL.md +66 -0
  141. package/plugins/skills/provenance/SKILL.md +70 -0
  142. package/plugins/skills/query/SKILL.md +84 -0
  143. package/plugins/skills/reason/SKILL.md +201 -0
  144. package/plugins/skills/temporal/SKILL.md +164 -0
  145. package/plugins/skills/validate/SKILL.md +228 -0
  146. package/plugins/skills/visualize/SKILL.md +249 -0
@@ -0,0 +1,231 @@
1
+ import type { CSSProperties } from "react";
2
+
3
+ import type { GraphPlugin } from "./types";
4
+
5
+ const NEIGHBORHOOD_PANEL_ID = "neighborhood-panel";
6
+ const MAX_NEIGHBORS = 10;
7
+
8
+ function maxWeightBetween(graphRef: any, sourceId: string, targetId: string): number {
9
+ let weight = 0;
10
+ graphRef.forEachDirectedEdge(sourceId, targetId, (_edgeId: string, attrs: { weight?: number }) => {
11
+ weight = Math.max(weight, Number(attrs.weight ?? 0));
12
+ });
13
+ return weight;
14
+ }
15
+
16
+ function formatNeighborMeta(neighbor: { nodeType: string; degree: number; weight: number }) {
17
+ const parts = [neighbor.nodeType, `degree ${neighbor.degree}`];
18
+ if (neighbor.weight > 0) {
19
+ parts.push(`weight ${neighbor.weight.toFixed(2)}`);
20
+ }
21
+ return parts.join(" · ");
22
+ }
23
+
24
+ export const neighborhoodPanelPlugin: GraphPlugin = {
25
+ id: "neighborhood-panel",
26
+ mount: () => {},
27
+ unmount: () => {},
28
+ onStateChange: () => {},
29
+ toolbarItems: (context) => [
30
+ {
31
+ id: "neighborhood-toggle",
32
+ label: "Neighbors",
33
+ title: "Toggle neighborhood panel",
34
+ active: context.isPanelOpen(NEIGHBORHOOD_PANEL_ID),
35
+ order: 30,
36
+ onClick: () => context.dispatchAction({ type: "togglePanel", panelId: NEIGHBORHOOD_PANEL_ID }),
37
+ },
38
+ ],
39
+ renderPanel: (context) => {
40
+ if (!context.isPanelOpen(NEIGHBORHOOD_PANEL_ID)) {
41
+ return null;
42
+ }
43
+
44
+ const selected = context.getSelectedNodeState();
45
+ const displayState = context.getDisplayState();
46
+ if (!selected) {
47
+ return {
48
+ id: NEIGHBORHOOD_PANEL_ID,
49
+ title: "Neighborhood",
50
+ placement: "bottom",
51
+ order: 20,
52
+ defaultOpen: false,
53
+ preferredWidth: 360,
54
+ preferredHeight: 260,
55
+ content: <div style={emptyTextStyle}>Select a node to inspect its local neighborhood.</div>,
56
+ };
57
+ }
58
+
59
+ const neighbors = context.graph
60
+ .neighbors(selected.id)
61
+ .map((neighborId) => {
62
+ const attrs = context.graph.getNodeAttributes(neighborId);
63
+ const weight = Math.max(
64
+ maxWeightBetween(context.graph, selected.id, neighborId),
65
+ maxWeightBetween(context.graph, neighborId, selected.id),
66
+ );
67
+ return {
68
+ id: neighborId,
69
+ label: String(attrs.label || neighborId),
70
+ nodeType: String(attrs.nodeType || "Entity"),
71
+ color: String(attrs.baseColor || attrs.color || context.theme.palette.semantic[0]),
72
+ weight,
73
+ degree: context.graph.degree(neighborId),
74
+ };
75
+ })
76
+ .sort((left, right) => {
77
+ if (right.weight !== left.weight) {
78
+ return right.weight - left.weight;
79
+ }
80
+ if (right.degree !== left.degree) {
81
+ return right.degree - left.degree;
82
+ }
83
+ return left.label.localeCompare(right.label);
84
+ })
85
+ .slice(0, MAX_NEIGHBORS);
86
+ const hiddenNeighborCount = displayState.selectedCollapsedNeighborIds.length;
87
+ const aggregatedEdgeCount = context.displayGraph
88
+ .edges()
89
+ .map((edgeId) => context.displayGraph.getEdgeAttributes(edgeId) as { isAggregated?: boolean })
90
+ .filter((attrs) => attrs.isAggregated).length;
91
+
92
+ return {
93
+ id: NEIGHBORHOOD_PANEL_ID,
94
+ title: "Neighborhood",
95
+ placement: "bottom",
96
+ order: 20,
97
+ defaultOpen: false,
98
+ preferredWidth: 360,
99
+ preferredHeight: 260,
100
+ content: (
101
+ <div style={panelBodyStyle}>
102
+ <div style={panelEyebrowStyle}>{selected.label}</div>
103
+ <div style={summaryStyle}>
104
+ {selected.neighborCount.toLocaleString()} direct neighbors in the full graph
105
+ </div>
106
+ <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
107
+ <button
108
+ type="button"
109
+ onClick={() => context.dispatchAction({ type: "collapseNeighborhood" })}
110
+ disabled={!selected.canCollapseNeighborhood || selected.isNeighborhoodCollapsed}
111
+ style={controlButtonStyle}
112
+ >
113
+ Collapse Neighborhood
114
+ </button>
115
+ <button
116
+ type="button"
117
+ onClick={() => context.dispatchAction({ type: "expandNeighborhood" })}
118
+ disabled={!selected.isNeighborhoodCollapsed}
119
+ style={controlButtonStyle}
120
+ >
121
+ Expand Neighborhood
122
+ </button>
123
+ </div>
124
+ {hiddenNeighborCount > 0 ? (
125
+ <div style={summaryStyle}>
126
+ {hiddenNeighborCount.toLocaleString()} lower-priority neighbors are collapsed in the current view.
127
+ </div>
128
+ ) : null}
129
+ {aggregatedEdgeCount > 0 ? (
130
+ <div style={summaryStyle}>
131
+ {aggregatedEdgeCount.toLocaleString()} aggregated structural bundle{aggregatedEdgeCount === 1 ? "" : "s"} visible.
132
+ </div>
133
+ ) : null}
134
+ {neighbors.length ? (
135
+ <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
136
+ {neighbors.map((neighbor) => (
137
+ <button
138
+ key={neighbor.id}
139
+ type="button"
140
+ onClick={() => context.dispatchAction({ type: "selectNode", nodeId: neighbor.id })}
141
+ style={neighborButtonStyle}
142
+ >
143
+ <span
144
+ style={{
145
+ ...swatchStyle,
146
+ background: neighbor.color,
147
+ boxShadow: `0 0 16px ${neighbor.color}40`,
148
+ }}
149
+ />
150
+ <div style={{ minWidth: 0, flex: 1, textAlign: "left" }}>
151
+ <div style={rowTitleStyle}>{neighbor.label}</div>
152
+ <div style={rowMetaStyle}>{formatNeighborMeta(neighbor)}</div>
153
+ </div>
154
+ </button>
155
+ ))}
156
+ </div>
157
+ ) : (
158
+ <div style={emptyTextStyle}>No direct neighbors are available for this node.</div>
159
+ )}
160
+ </div>
161
+ ),
162
+ };
163
+ },
164
+ };
165
+
166
+ const panelBodyStyle: CSSProperties = {
167
+ display: "flex",
168
+ flexDirection: "column",
169
+ gap: 12,
170
+ };
171
+
172
+ const panelEyebrowStyle: CSSProperties = {
173
+ color: "#fff1f3",
174
+ fontSize: 14,
175
+ fontWeight: 700,
176
+ };
177
+
178
+ const summaryStyle: CSSProperties = {
179
+ color: "#c99aa3",
180
+ fontSize: 12,
181
+ lineHeight: 1.5,
182
+ };
183
+
184
+ const neighborButtonStyle: CSSProperties = {
185
+ display: "flex",
186
+ alignItems: "center",
187
+ gap: 10,
188
+ width: "100%",
189
+ padding: "8px 10px",
190
+ background: "rgba(255,255,255,0.025)",
191
+ border: "1px solid rgba(255,255,255,0.06)",
192
+ borderRadius: 12,
193
+ cursor: "pointer",
194
+ };
195
+
196
+ const controlButtonStyle: CSSProperties = {
197
+ padding: "7px 10px",
198
+ background: "rgba(255,255,255,0.03)",
199
+ border: "1px solid rgba(255,255,255,0.08)",
200
+ borderRadius: 10,
201
+ color: "#dce7f4",
202
+ cursor: "pointer",
203
+ fontSize: 12,
204
+ };
205
+
206
+ const swatchStyle: CSSProperties = {
207
+ width: 10,
208
+ height: 10,
209
+ borderRadius: 999,
210
+ flexShrink: 0,
211
+ };
212
+
213
+ const rowTitleStyle: CSSProperties = {
214
+ color: "#fff1f3",
215
+ fontSize: 13,
216
+ fontWeight: 600,
217
+ overflow: "hidden",
218
+ textOverflow: "ellipsis",
219
+ whiteSpace: "nowrap",
220
+ };
221
+
222
+ const rowMetaStyle: CSSProperties = {
223
+ color: "#c99aa3",
224
+ fontSize: 12,
225
+ };
226
+
227
+ const emptyTextStyle: CSSProperties = {
228
+ color: "#c99aa3",
229
+ fontSize: 12,
230
+ lineHeight: 1.5,
231
+ };
@@ -0,0 +1,142 @@
1
+ import type { CSSProperties } from "react";
2
+
3
+ import type { GraphPlugin } from "./types";
4
+
5
+ const TEMPORAL_PANEL_ID = "temporal-panel";
6
+
7
+ function formatTemporalLabel(value: Date | null) {
8
+ if (!value) {
9
+ return "No time selected";
10
+ }
11
+ return `${value.getFullYear()}/${String(value.getMonth() + 1).padStart(2, "0")}`;
12
+ }
13
+
14
+ export const temporalOverlayPlugin: GraphPlugin = {
15
+ id: "temporal-overlay",
16
+ mount: () => {},
17
+ unmount: () => {},
18
+ onStateChange: () => {},
19
+ toolbarItems: (context) => [
20
+ {
21
+ id: "temporal-toggle",
22
+ label: "Temporal",
23
+ title: "Toggle temporal context panel",
24
+ active: context.isPanelOpen(TEMPORAL_PANEL_ID),
25
+ order: 40,
26
+ onClick: () => context.dispatchAction({ type: "togglePanel", panelId: TEMPORAL_PANEL_ID }),
27
+ },
28
+ ],
29
+ renderOverlay: (context) => {
30
+ const temporal = context.getTemporalState();
31
+ if (!temporal?.currentTime) {
32
+ return null;
33
+ }
34
+
35
+ const label = formatTemporalLabel(temporal.currentTime);
36
+ return {
37
+ id: "temporal-overlay-chip",
38
+ layer: 1,
39
+ order: 10,
40
+ element: (
41
+ <div
42
+ style={{
43
+ position: "absolute",
44
+ left: 140,
45
+ bottom: 26,
46
+ display: "inline-flex",
47
+ alignItems: "center",
48
+ gap: 10,
49
+ padding: "8px 12px",
50
+ borderRadius: 999,
51
+ border: "1px solid rgba(255, 107, 129, 0.18)",
52
+ background: "linear-gradient(135deg, rgba(6, 15, 27, 0.88), rgba(11, 22, 39, 0.76))",
53
+ boxShadow: "0 12px 30px rgba(0, 0, 0, 0.28)",
54
+ color: "#ffdfe4",
55
+ fontSize: 11,
56
+ letterSpacing: "0.05em",
57
+ textTransform: "uppercase",
58
+ pointerEvents: "none",
59
+ }}
60
+ >
61
+ <span style={{ color: "#7fc6ff", fontWeight: 700 }}>Temporal</span>
62
+ <span>{label}</span>
63
+ {typeof temporal.activeNodeCount === "number" ? (
64
+ <span style={{ color: "#c99aa3" }}>{temporal.activeNodeCount.toLocaleString()} active</span>
65
+ ) : null}
66
+ </div>
67
+ ),
68
+ };
69
+ },
70
+ renderPanel: (context) => {
71
+ if (!context.isPanelOpen(TEMPORAL_PANEL_ID)) {
72
+ return null;
73
+ }
74
+
75
+ const temporal = context.getTemporalState();
76
+ return {
77
+ id: TEMPORAL_PANEL_ID,
78
+ title: "Temporal Context",
79
+ placement: "bottom",
80
+ order: 30,
81
+ defaultOpen: false,
82
+ preferredWidth: 320,
83
+ preferredHeight: 220,
84
+ content: (
85
+ <div style={panelBodyStyle}>
86
+ <div style={panelEyebrowStyle}>Current scrubber state</div>
87
+ <div style={detailRowStyle}>
88
+ <span style={detailLabelStyle}>Current</span>
89
+ <span style={detailValueStyle}>{formatTemporalLabel(temporal?.currentTime ?? null)}</span>
90
+ </div>
91
+ <div style={detailRowStyle}>
92
+ <span style={detailLabelStyle}>Bounds</span>
93
+ <span style={detailValueStyle}>
94
+ {(temporal?.minDate ?? "1970")} → {(temporal?.maxDate ?? "now")}
95
+ </span>
96
+ </div>
97
+ <div style={detailRowStyle}>
98
+ <span style={detailLabelStyle}>Active nodes</span>
99
+ <span style={detailValueStyle}>
100
+ {typeof temporal?.activeNodeCount === "number" ? temporal.activeNodeCount.toLocaleString() : "All"}
101
+ </span>
102
+ </div>
103
+ </div>
104
+ ),
105
+ };
106
+ },
107
+ };
108
+
109
+ const panelBodyStyle: CSSProperties = {
110
+ display: "flex",
111
+ flexDirection: "column",
112
+ gap: 10,
113
+ };
114
+
115
+ const panelEyebrowStyle: CSSProperties = {
116
+ color: "#c99aa3",
117
+ fontSize: 11,
118
+ fontWeight: 700,
119
+ letterSpacing: "0.08em",
120
+ textTransform: "uppercase",
121
+ };
122
+
123
+ const detailRowStyle: CSSProperties = {
124
+ display: "flex",
125
+ justifyContent: "space-between",
126
+ gap: 16,
127
+ padding: "8px 10px",
128
+ borderRadius: 12,
129
+ border: "1px solid rgba(255,255,255,0.06)",
130
+ background: "rgba(255,255,255,0.025)",
131
+ };
132
+
133
+ const detailLabelStyle: CSSProperties = {
134
+ color: "#c99aa3",
135
+ fontSize: 12,
136
+ };
137
+
138
+ const detailValueStyle: CSSProperties = {
139
+ color: "#fff1f3",
140
+ fontSize: 12,
141
+ fontWeight: 600,
142
+ };
@@ -0,0 +1,106 @@
1
+ import type { ReactNode } from "react";
2
+ import type Graph from "graphology";
3
+
4
+ import { graph, type EdgeAttributes, type NodeAttributes } from "../../../store/graphStore";
5
+ import type { GraphTheme } from "../graphTheme";
6
+ import type { GraphSceneRuntime } from "../scene";
7
+ import type {
8
+ GraphAnalyticsSnapshot,
9
+ GraphDisplayStateSnapshot,
10
+ GraphDiagnosticsSnapshot,
11
+ GraphEffectsState,
12
+ GraphEffectToggle,
13
+ GraphInteractionState,
14
+ GraphLoadSummary,
15
+ GraphSelectedNodeState,
16
+ GraphTemporalState,
17
+ GraphLayoutViewMode,
18
+ } from "../types";
19
+
20
+ export type { GraphTemporalState } from "../types";
21
+
22
+ export type GraphPluginId = string;
23
+ export type GraphPluginPanelPlacement = "side" | "bottom";
24
+
25
+ export interface GraphInspectorState {
26
+ selectedNodeId: string | null;
27
+ ownsSelectionDetails: boolean;
28
+ }
29
+
30
+ export type GraphPluginActionRequest =
31
+ | { type: "fitView" }
32
+ | { type: "focusNode"; nodeId: string }
33
+ | { type: "selectNode"; nodeId: string }
34
+ | { type: "setViewMode"; viewMode: GraphLayoutViewMode }
35
+ | { type: "enterLocalGraph"; nodeId: string }
36
+ | { type: "collapseNeighborhood" }
37
+ | { type: "expandNeighborhood" }
38
+ | { type: "toggleEffect"; effect: GraphEffectToggle }
39
+ | { type: "setEffect"; effect: GraphEffectToggle; enabled: boolean }
40
+ | { type: "togglePanel"; panelId: string }
41
+ | { type: "openPanel"; panelId: string }
42
+ | { type: "closePanel"; panelId: string };
43
+
44
+ export interface GraphPluginToolbarItem {
45
+ id: string;
46
+ label: string;
47
+ title?: string;
48
+ active?: boolean;
49
+ order?: number;
50
+ onClick: () => void;
51
+ }
52
+
53
+ export interface GraphPluginPanelDescriptor {
54
+ id: string;
55
+ title: string;
56
+ placement: GraphPluginPanelPlacement;
57
+ order?: number;
58
+ defaultOpen?: boolean;
59
+ preferredHeight?: number;
60
+ preferredWidth?: number;
61
+ content: ReactNode;
62
+ }
63
+
64
+ export interface GraphPluginOverlayDescriptor {
65
+ id: string;
66
+ layer?: number;
67
+ order?: number;
68
+ element: ReactNode;
69
+ }
70
+
71
+ export interface GraphPluginContext {
72
+ readonly scene: GraphSceneRuntime | null;
73
+ readonly graph: typeof graph | Graph<NodeAttributes, EdgeAttributes>;
74
+ readonly displayGraph: typeof graph | Graph<NodeAttributes, EdgeAttributes>;
75
+ readonly theme: GraphTheme;
76
+ getInteractionState: () => GraphInteractionState;
77
+ getSelectedNodeState: () => GraphSelectedNodeState | null;
78
+ getInspectorState: () => GraphInspectorState;
79
+ getGraphSummary: () => GraphLoadSummary | null;
80
+ getTemporalState: () => GraphTemporalState | null;
81
+ getEffectsState: () => GraphEffectsState;
82
+ getDiagnosticsSnapshot: () => GraphDiagnosticsSnapshot | null;
83
+ getAnalyticsSnapshot: () => GraphAnalyticsSnapshot | null;
84
+ getDisplayState: () => GraphDisplayStateSnapshot;
85
+ isPanelOpen: (panelId: string) => boolean;
86
+ dispatchAction: (action: GraphPluginActionRequest) => void;
87
+ }
88
+
89
+ export interface GraphPlugin {
90
+ id: GraphPluginId;
91
+ mount: (context: GraphPluginContext) => void;
92
+ unmount: (context: GraphPluginContext) => void;
93
+ onStateChange: (context: GraphPluginContext, interactionState: GraphInteractionState) => void;
94
+ renderOverlay?: (
95
+ context: GraphPluginContext,
96
+ ) => GraphPluginOverlayDescriptor | GraphPluginOverlayDescriptor[] | null;
97
+ renderPanel?: (
98
+ context: GraphPluginContext,
99
+ ) => GraphPluginPanelDescriptor | GraphPluginPanelDescriptor[] | null;
100
+ toolbarItems?: (context: GraphPluginContext) => GraphPluginToolbarItem[];
101
+ }
102
+
103
+ export interface GraphPluginRegistryEntry {
104
+ plugin: GraphPlugin;
105
+ enabled?: boolean;
106
+ }
@@ -0,0 +1,50 @@
1
+ import type { EdgeAttributes } from "../../store/graphStore";
2
+ import { curveGroupForPair } from "../../store/edgePairKeys.js";
3
+ import { GRAPH_THEME } from "./graphTheme";
4
+
5
+ export type RealtimeEdgePayload = {
6
+ id: string;
7
+ familyId?: string;
8
+ source_id: string;
9
+ target_id: string;
10
+ type?: string;
11
+ weight?: number;
12
+ properties?: Record<string, unknown>;
13
+ };
14
+
15
+ export function buildRealtimeEdgeAttributes(
16
+ payload: RealtimeEdgePayload,
17
+ options: { isBidirectional: boolean; isSmallGraph: boolean },
18
+ ): EdgeAttributes {
19
+ const properties = payload.properties || {};
20
+ const isInferred = Boolean(properties.inferred);
21
+ const baseColor = isInferred ? GRAPH_THEME.palette.accent.path : GRAPH_THEME.palette.muted.edgeStructure;
22
+
23
+ return {
24
+ edgeId: payload.id,
25
+ familyId: payload.familyId || payload.id,
26
+ sourceId: payload.source_id,
27
+ targetId: payload.target_id,
28
+ weight: Number(payload.weight ?? 1),
29
+ edgeType: payload.type || "related_to",
30
+ properties,
31
+ size: 1,
32
+ baseSize: 1,
33
+ color: baseColor,
34
+ baseColor,
35
+ mutedColor: GRAPH_THEME.palette.muted.edgeOverview,
36
+ visualPriority: isInferred ? 0.95 : 0.5,
37
+ isBidirectional: options.isBidirectional,
38
+ edgeFamily: isInferred ? "path" : options.isBidirectional ? "bidirectional" : "line",
39
+ curveGroup: options.isBidirectional ? curveGroupForPair(payload.source_id, payload.target_id) : null,
40
+ type: "line",
41
+ edgeVariant: isInferred ? "pathSignal" : options.isBidirectional ? "bidirectionalCurve" : "directional",
42
+ arrowVisibilityPolicy: isInferred ? "always" : "contextual",
43
+ relationshipStrength: isInferred ? 0.95 : 0.52,
44
+ isParallelPair: false,
45
+ parallelIndex: 0,
46
+ parallelCount: 1,
47
+ familySize: 1,
48
+ isSmallGraph: options.isSmallGraph,
49
+ };
50
+ }
@@ -0,0 +1,79 @@
1
+ import type { ForwardRefExoticComponent, ReactNode, RefAttributes } from "react";
2
+ import type Graph from "graphology";
3
+
4
+ import { graph, type EdgeAttributes, type NodeAttributes } from "../../store/graphStore";
5
+ import type {
6
+ GraphAnalyticsSnapshot,
7
+ GraphCameraState,
8
+ GraphDistanceVisualState,
9
+ GraphDisplayMeta,
10
+ GraphDisplayStateSnapshot,
11
+ GraphEffectsState,
12
+ GraphInteractionState,
13
+ GraphLayoutSource,
14
+ GraphLayoutStatus,
15
+ GraphRuntimeDiagnosticsSnapshot,
16
+ GraphTemporalState,
17
+ GraphViewMode,
18
+ } from "./types";
19
+
20
+ export type GraphSceneGraph = typeof graph | Graph<NodeAttributes, EdgeAttributes>;
21
+ export type GraphSceneRenderer = "sigma";
22
+
23
+ export interface GraphSceneRuntime {
24
+ renderer: GraphSceneRenderer;
25
+ scene: unknown;
26
+ graph: GraphSceneGraph;
27
+ displayGraph: GraphSceneGraph;
28
+ graphVersion: number;
29
+ layoutMode?: GraphDisplayMeta["layoutMode"];
30
+ requestRender: () => void;
31
+ getCameraState: () => GraphCameraState | null;
32
+ }
33
+
34
+ export interface GraphSceneEventMap {
35
+ onNodeSelect?: (nodeId: string) => void;
36
+ onEdgeSelect?: (edgeId: string) => void;
37
+ onInteractionStateChange?: (interactionState: GraphInteractionState) => void;
38
+ onCameraStateChange?: (cameraState: GraphCameraState) => void;
39
+ onDiagnosticsChange?: (diagnostics: GraphRuntimeDiagnosticsSnapshot) => void;
40
+ onAnalyticsChange?: (analytics: GraphAnalyticsSnapshot | null) => void;
41
+ onRuntimeChange?: (runtime: GraphSceneRuntime | null) => void;
42
+ }
43
+
44
+ export interface GraphSceneProps extends GraphSceneEventMap {
45
+ graphVersion: number;
46
+ graphReady: boolean;
47
+ displayGraph: GraphSceneGraph;
48
+ displayMeta: GraphDisplayMeta;
49
+ displayState?: GraphDisplayStateSnapshot;
50
+ selectedNodeId: string;
51
+ focusedNodeId: string;
52
+ selectedEdgeId: string;
53
+ activePath?: string[];
54
+ activePathEdgeIds?: string[];
55
+ distanceVisualState?: GraphDistanceVisualState;
56
+ effectsState: GraphEffectsState;
57
+ temporalState?: GraphTemporalState | null;
58
+ isLayoutRunning: boolean;
59
+ onLayoutRunningChange?: (running: boolean) => void;
60
+ layoutSource?: GraphLayoutSource;
61
+ onLayoutStatusChange?: (status: GraphLayoutStatus) => void;
62
+ viewMode: GraphViewMode;
63
+ className?: string;
64
+ showFitViewButton?: boolean;
65
+ pluginOverlays?: ReactNode[];
66
+ }
67
+
68
+ export interface GraphSceneHandle {
69
+ fitView: () => void;
70
+ focusNode: (nodeId: string) => void;
71
+ zoomIn: () => void;
72
+ zoomOut: () => void;
73
+ getRuntime: () => GraphSceneRuntime | null;
74
+ setLayoutRunning?: (running: boolean) => void;
75
+ }
76
+
77
+ export type GraphSceneAdapter = ForwardRefExoticComponent<
78
+ GraphSceneProps & RefAttributes<GraphSceneHandle>
79
+ >;