@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,522 @@
1
+ import type { CSSProperties } from "react";
2
+
3
+ import { buildGraphColorLegend } from "../graphColorLegend";
4
+ import type {
5
+ GraphAnalyticsSnapshot,
6
+ GraphDiagnosticsSnapshot,
7
+ GraphEffectAvailability,
8
+ GraphEffectToggle,
9
+ } from "../types";
10
+ import type { GraphPlugin } from "./types";
11
+
12
+ const EFFECTS_PANEL_ID = "effects-panel";
13
+
14
+ type EffectRowConfig = {
15
+ key: GraphEffectToggle;
16
+ label: string;
17
+ description: string;
18
+ };
19
+
20
+ const SCENE_EFFECT_ROWS: EffectRowConfig[] = [
21
+ {
22
+ key: "pathPulseEnabled",
23
+ label: "Path Pulse",
24
+ description: "Animated pulse on the active selected path.",
25
+ },
26
+ {
27
+ key: "pathFlowEnabled",
28
+ label: "Path Flow",
29
+ description: "Directional flow accents along the active selected path.",
30
+ },
31
+ {
32
+ key: "lensEnabled",
33
+ label: "Neighborhood Lens",
34
+ description: "Local emphasis around the hovered or selected node.",
35
+ },
36
+ {
37
+ key: "temporalEmphasisEnabled",
38
+ label: "Temporal Emphasis",
39
+ description: "Subtle glow around temporally relevant nodes in the active time window.",
40
+ },
41
+ {
42
+ key: "semanticRegionsEnabled",
43
+ label: "Semantic Regions",
44
+ description: "Quiet semantic hulls around the strongest visible topic clusters.",
45
+ },
46
+ {
47
+ key: "contoursEnabled",
48
+ label: "Contours",
49
+ description: "Low-contrast density halos around the strongest visible anchors.",
50
+ },
51
+ {
52
+ key: "edgeLabelsEnabled",
53
+ label: "Edge Labels",
54
+ description: "Draw the relationship type on graph edges. Off restores label-free edges on dense graphs.",
55
+ },
56
+ {
57
+ key: "legendEnabled",
58
+ label: "Regions Summary",
59
+ description: "Keep the regions and signals summary visible in the Effects panel.",
60
+ },
61
+ ];
62
+
63
+ const INTELLIGENCE_EFFECT_ROWS: EffectRowConfig[] = [
64
+ {
65
+ key: "pathfindingEnabled",
66
+ label: "Directed Pathfinding",
67
+ description: "Compare the traced path against a strict local directed shortest path.",
68
+ },
69
+ {
70
+ key: "communitiesEnabled",
71
+ label: "Community Regions",
72
+ description: "Detect stable Louvain communities for orientation and scene grouping.",
73
+ },
74
+ {
75
+ key: "centralityEnabled",
76
+ label: "Centrality Ranking",
77
+ description: "Rank the strongest graph anchors for labels, regions, and navigation.",
78
+ },
79
+ ];
80
+
81
+ const AVAILABILITY_KEYS: Record<GraphEffectToggle, keyof GraphDiagnosticsSnapshot["effectAvailability"]> = {
82
+ pathPulseEnabled: "pathPulse",
83
+ pathFlowEnabled: "pathFlow",
84
+ lensEnabled: "lens",
85
+ temporalEmphasisEnabled: "temporalEmphasis",
86
+ semanticRegionsEnabled: "semanticRegions",
87
+ contoursEnabled: "contours",
88
+ pathfindingEnabled: "pathfinding",
89
+ communitiesEnabled: "communities",
90
+ centralityEnabled: "centrality",
91
+ legendEnabled: "legend",
92
+ edgeLabelsEnabled: "edgeLabels",
93
+ diagnosticsEnabled: "diagnostics",
94
+ };
95
+
96
+ function renderAvailabilityText(availability: GraphEffectAvailability) {
97
+ if (availability.available) {
98
+ if (typeof availability.visibleSegments === "number" && typeof availability.segmentCap === "number") {
99
+ return `${availability.reason} - ${availability.visibleSegments}/${availability.segmentCap} segments`;
100
+ }
101
+ return availability.reason;
102
+ }
103
+
104
+ return availability.detail ? `${availability.reason} - ${availability.detail}` : availability.reason;
105
+ }
106
+
107
+ function collectFallbackLegendItems(context: Parameters<NonNullable<GraphPlugin["renderPanel"]>>[0]) {
108
+ return buildGraphColorLegend(context.graph, context.theme)
109
+ .sort((left, right) => right.count - left.count)
110
+ .slice(0, context.theme.effects.legend.maxGroups);
111
+ }
112
+
113
+ function resolveAvailability(
114
+ availabilityMap: GraphDiagnosticsSnapshot["effectAvailability"] | undefined,
115
+ key: GraphEffectToggle,
116
+ enabled: boolean,
117
+ ): GraphEffectAvailability {
118
+ return availabilityMap?.[AVAILABILITY_KEYS[key]] ?? {
119
+ enabled,
120
+ available: false,
121
+ reason: "Waiting for graph runtime",
122
+ };
123
+ }
124
+
125
+ function EffectToggleRow({
126
+ label,
127
+ description,
128
+ checked,
129
+ availability,
130
+ onToggle,
131
+ }: {
132
+ label: string;
133
+ description: string;
134
+ checked: boolean;
135
+ availability: GraphEffectAvailability;
136
+ onToggle: () => void;
137
+ }) {
138
+ return (
139
+ <div style={toggleRowStyle}>
140
+ <div style={{ minWidth: 0, flex: 1 }}>
141
+ <div style={rowTitleStyle}>{label}</div>
142
+ <div style={rowDescriptionStyle}>{description}</div>
143
+ <div style={rowMetaStyle}>{renderAvailabilityText(availability)}</div>
144
+ </div>
145
+ <button type="button" onClick={onToggle} style={checked ? toggleButtonActiveStyle : toggleButtonStyle}>
146
+ {checked ? "On" : "Off"}
147
+ </button>
148
+ </div>
149
+ );
150
+ }
151
+
152
+ function renderRegionsAndSignals(
153
+ context: Parameters<NonNullable<GraphPlugin["renderPanel"]>>[0],
154
+ analytics: GraphAnalyticsSnapshot | null,
155
+ ) {
156
+ const fallbackLegendItems = collectFallbackLegendItems(context);
157
+ const semanticRegions = analytics?.semanticRegions.summaries ?? [];
158
+ const communities = analytics?.communities.summaries ?? [];
159
+ const centrality = analytics?.centrality.topNodes ?? [];
160
+ const directedPath = analytics?.directedPath ?? null;
161
+
162
+ if (!semanticRegions.length && !communities.length && !centrality.length && !fallbackLegendItems.length && !directedPath) {
163
+ return <div style={emptyTextStyle}>Regions and intelligence summaries will populate when graph analytics are ready.</div>;
164
+ }
165
+
166
+ return (
167
+ <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
168
+ {semanticRegions.length ? (
169
+ <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
170
+ <div style={subsectionTitleStyle}>Semantic regions</div>
171
+ {semanticRegions.map((region) => (
172
+ <div key={region.semanticGroup} style={legendRowStyle}>
173
+ <span
174
+ style={{
175
+ ...legendSwatchStyle,
176
+ background: region.color,
177
+ boxShadow: `0 0 0 1px rgba(255,255,255,0.06), 0 0 14px ${region.color}40`,
178
+ }}
179
+ />
180
+ <div style={{ minWidth: 0, flex: 1 }}>
181
+ <div style={rowTitleStyle}>{region.semanticGroup}</div>
182
+ <div style={rowMetaStyle}>
183
+ {region.visibleNodeCount.toLocaleString()} visible / {region.nodeCount.toLocaleString()} total
184
+ </div>
185
+ </div>
186
+ <div style={signalBadgeStyle}>{region.anchorLabel}</div>
187
+ </div>
188
+ ))}
189
+ </div>
190
+ ) : null}
191
+
192
+ {communities.length ? (
193
+ <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
194
+ <div style={subsectionTitleStyle}>Community anchors</div>
195
+ {communities.slice(0, 3).map((community) => (
196
+ <div key={community.communityId} style={signalRowStyle}>
197
+ <div style={{ minWidth: 0, flex: 1 }}>
198
+ <div style={rowTitleStyle}>{community.anchorLabel}</div>
199
+ <div style={rowMetaStyle}>
200
+ Community {community.communityId} - {community.visibleNodeCount} visible / {community.nodeCount} total
201
+ </div>
202
+ </div>
203
+ <div style={signalBadgeStyle}>{community.dominantSemanticGroup}</div>
204
+ </div>
205
+ ))}
206
+ </div>
207
+ ) : null}
208
+
209
+ {centrality.length ? (
210
+ <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
211
+ <div style={subsectionTitleStyle}>Centrality leaders</div>
212
+ {centrality.slice(0, 3).map((node) => (
213
+ <div key={node.id} style={signalRowStyle}>
214
+ <div style={{ minWidth: 0, flex: 1 }}>
215
+ <div style={rowTitleStyle}>{node.label}</div>
216
+ <div style={rowMetaStyle}>
217
+ {node.semanticGroup} - score {node.score.toFixed(3)}
218
+ </div>
219
+ </div>
220
+ <div style={signalBadgeStyle}>deg {node.degree.toFixed(3)}</div>
221
+ </div>
222
+ ))}
223
+ </div>
224
+ ) : null}
225
+
226
+ {directedPath ? (
227
+ <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
228
+ <div style={subsectionTitleStyle}>Directed pathfinding</div>
229
+ <div style={signalRowStyle}>
230
+ <div style={{ minWidth: 0, flex: 1 }}>
231
+ <div style={rowTitleStyle}>{directedPath.ready ? "Local directed path ready" : "Waiting for path context"}</div>
232
+ <div style={rowMetaStyle}>{directedPath.reason}</div>
233
+ </div>
234
+ {directedPath.ready ? (
235
+ <div style={signalBadgeStyle}>
236
+ {directedPath.length} hops{directedPath.verifiedAgainstActivePath ? " - match" : ""}
237
+ </div>
238
+ ) : null}
239
+ </div>
240
+ </div>
241
+ ) : null}
242
+
243
+ {!semanticRegions.length && !communities.length && !centrality.length && fallbackLegendItems.length ? (
244
+ <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
245
+ <div style={subsectionTitleStyle}>Fallback semantic legend</div>
246
+ {fallbackLegendItems.map((item) => (
247
+ <div key={item.id} style={legendRowStyle}>
248
+ <span
249
+ style={{
250
+ ...legendSwatchStyle,
251
+ background: item.color,
252
+ boxShadow: `0 0 0 1px rgba(255,255,255,0.06), 0 0 14px ${item.color}40`,
253
+ }}
254
+ />
255
+ <div style={{ minWidth: 0, flex: 1 }}>
256
+ <div style={rowTitleStyle}>{item.group}</div>
257
+ <div style={rowMetaStyle}>{item.count.toLocaleString()} nodes</div>
258
+ </div>
259
+ </div>
260
+ ))}
261
+ </div>
262
+ ) : null}
263
+ </div>
264
+ );
265
+ }
266
+
267
+ export const explorationEffectsPluginPhaseC: GraphPlugin = {
268
+ id: "exploration-effects",
269
+ mount: () => {},
270
+ unmount: () => {},
271
+ onStateChange: () => {},
272
+ toolbarItems: (context) => [
273
+ {
274
+ id: "effects-toggle",
275
+ label: "Effects",
276
+ title: "Open exploration effects controls",
277
+ active: context.isPanelOpen(EFFECTS_PANEL_ID),
278
+ order: 18,
279
+ onClick: () => context.dispatchAction({ type: "togglePanel", panelId: EFFECTS_PANEL_ID }),
280
+ },
281
+ ],
282
+ renderPanel: (context) => {
283
+ if (!context.isPanelOpen(EFFECTS_PANEL_ID)) {
284
+ return null;
285
+ }
286
+
287
+ const effectsState = context.getEffectsState();
288
+ const diagnosticsSnapshot = context.getDiagnosticsSnapshot();
289
+ const analyticsSnapshot = context.getAnalyticsSnapshot();
290
+ const availability = diagnosticsSnapshot?.effectAvailability;
291
+ const showSignalsSection =
292
+ effectsState.legendEnabled
293
+ || effectsState.semanticRegionsEnabled
294
+ || effectsState.communitiesEnabled
295
+ || effectsState.centralityEnabled
296
+ || effectsState.pathfindingEnabled;
297
+
298
+ return {
299
+ id: EFFECTS_PANEL_ID,
300
+ title: "Effects",
301
+ placement: "bottom",
302
+ order: 8,
303
+ defaultOpen: false,
304
+ preferredWidth: 460,
305
+ preferredHeight: 360,
306
+ content: (
307
+ <div style={panelBodyStyle}>
308
+ <div style={panelEyebrowStyle}>Exploration effects</div>
309
+
310
+ <div style={sectionStyle}>
311
+ <div style={sectionTitleStyle}>Scene effects</div>
312
+ {SCENE_EFFECT_ROWS.map((row) => (
313
+ <EffectToggleRow
314
+ key={row.key}
315
+ label={row.label}
316
+ description={row.description}
317
+ checked={effectsState[row.key]}
318
+ availability={resolveAvailability(availability, row.key, effectsState[row.key])}
319
+ onToggle={() => context.dispatchAction({ type: "toggleEffect", effect: row.key })}
320
+ />
321
+ ))}
322
+ </div>
323
+
324
+ <div style={sectionStyle}>
325
+ <div style={sectionTitleStyle}>Graph intelligence</div>
326
+ {INTELLIGENCE_EFFECT_ROWS.map((row) => (
327
+ <EffectToggleRow
328
+ key={row.key}
329
+ label={row.label}
330
+ description={row.description}
331
+ checked={effectsState[row.key]}
332
+ availability={resolveAvailability(availability, row.key, effectsState[row.key])}
333
+ onToggle={() => context.dispatchAction({ type: "toggleEffect", effect: row.key })}
334
+ />
335
+ ))}
336
+ </div>
337
+
338
+ {showSignalsSection ? (
339
+ <div style={sectionStyle}>
340
+ <div style={sectionTitleStyle}>Regions and signals</div>
341
+ {renderRegionsAndSignals(context, analyticsSnapshot)}
342
+ </div>
343
+ ) : null}
344
+
345
+ {import.meta.env.DEV ? (
346
+ <div style={sectionStyle}>
347
+ <div style={sectionTitleStyle}>Diagnostics</div>
348
+ <EffectToggleRow
349
+ label="Dev Diagnostics"
350
+ description="Inspect plugin, interaction, and effect gating state."
351
+ checked={effectsState.diagnosticsEnabled}
352
+ availability={resolveAvailability(availability, "diagnosticsEnabled", effectsState.diagnosticsEnabled)}
353
+ onToggle={() => context.dispatchAction({ type: "toggleEffect", effect: "diagnosticsEnabled" })}
354
+ />
355
+ {effectsState.diagnosticsEnabled && diagnosticsSnapshot ? (
356
+ <details style={detailsStyle}>
357
+ <summary style={summaryStyle}>Runtime snapshot</summary>
358
+ <pre style={diagnosticsPreStyle}>
359
+ {JSON.stringify({ diagnostics: diagnosticsSnapshot, analytics: analyticsSnapshot }, null, 2)}
360
+ </pre>
361
+ </details>
362
+ ) : null}
363
+ </div>
364
+ ) : null}
365
+ </div>
366
+ ),
367
+ };
368
+ },
369
+ };
370
+
371
+ const panelBodyStyle: CSSProperties = {
372
+ display: "flex",
373
+ flexDirection: "column",
374
+ gap: 12,
375
+ };
376
+
377
+ const panelEyebrowStyle: CSSProperties = {
378
+ color: "#c99aa3",
379
+ fontSize: 11,
380
+ fontWeight: 700,
381
+ letterSpacing: "0.08em",
382
+ textTransform: "uppercase",
383
+ };
384
+
385
+ const sectionStyle: CSSProperties = {
386
+ display: "flex",
387
+ flexDirection: "column",
388
+ gap: 8,
389
+ padding: "10px 12px",
390
+ borderRadius: 14,
391
+ border: "1px solid rgba(255,255,255,0.06)",
392
+ background: "rgba(255,255,255,0.025)",
393
+ };
394
+
395
+ const sectionTitleStyle: CSSProperties = {
396
+ color: "#ffdfe4",
397
+ fontSize: 12,
398
+ fontWeight: 700,
399
+ };
400
+
401
+ const subsectionTitleStyle: CSSProperties = {
402
+ color: "#9cc4ec",
403
+ fontSize: 11,
404
+ fontWeight: 700,
405
+ letterSpacing: "0.05em",
406
+ textTransform: "uppercase",
407
+ };
408
+
409
+ const toggleRowStyle: CSSProperties = {
410
+ display: "flex",
411
+ alignItems: "center",
412
+ gap: 12,
413
+ padding: "8px 0",
414
+ };
415
+
416
+ const rowTitleStyle: CSSProperties = {
417
+ color: "#fff1f3",
418
+ fontSize: 13,
419
+ fontWeight: 600,
420
+ };
421
+
422
+ const rowDescriptionStyle: CSSProperties = {
423
+ color: "#a1b7cf",
424
+ fontSize: 12,
425
+ lineHeight: 1.45,
426
+ };
427
+
428
+ const rowMetaStyle: CSSProperties = {
429
+ color: "#7fc6ff",
430
+ fontSize: 11,
431
+ lineHeight: 1.45,
432
+ };
433
+
434
+ const toggleButtonStyle: CSSProperties = {
435
+ minWidth: 52,
436
+ padding: "8px 10px",
437
+ borderRadius: 999,
438
+ border: "1px solid rgba(255,255,255,0.08)",
439
+ background: "rgba(255,255,255,0.03)",
440
+ color: "#cfe0f4",
441
+ fontSize: 12,
442
+ fontWeight: 700,
443
+ cursor: "pointer",
444
+ };
445
+
446
+ const toggleButtonActiveStyle: CSSProperties = {
447
+ ...toggleButtonStyle,
448
+ background: "rgba(31, 111, 235, 0.24)",
449
+ border: "1px solid rgba(255, 107, 129, 0.28)",
450
+ color: "#eef6ff",
451
+ };
452
+
453
+ const legendRowStyle: CSSProperties = {
454
+ display: "flex",
455
+ alignItems: "center",
456
+ gap: 10,
457
+ padding: "8px 10px",
458
+ borderRadius: 12,
459
+ border: "1px solid rgba(255,255,255,0.06)",
460
+ background: "rgba(255,255,255,0.025)",
461
+ };
462
+
463
+ const legendSwatchStyle: CSSProperties = {
464
+ width: 10,
465
+ height: 10,
466
+ borderRadius: 999,
467
+ flexShrink: 0,
468
+ };
469
+
470
+ const signalRowStyle: CSSProperties = {
471
+ display: "flex",
472
+ alignItems: "center",
473
+ gap: 10,
474
+ padding: "8px 10px",
475
+ borderRadius: 12,
476
+ border: "1px solid rgba(255,255,255,0.05)",
477
+ background: "rgba(255,255,255,0.02)",
478
+ };
479
+
480
+ const signalBadgeStyle: CSSProperties = {
481
+ padding: "5px 8px",
482
+ borderRadius: 999,
483
+ background: "rgba(31, 111, 235, 0.16)",
484
+ border: "1px solid rgba(255, 107, 129, 0.16)",
485
+ color: "#ffdfe4",
486
+ fontSize: 11,
487
+ fontWeight: 700,
488
+ whiteSpace: "nowrap",
489
+ };
490
+
491
+ const detailsStyle: CSSProperties = {
492
+ borderRadius: 12,
493
+ border: "1px solid rgba(255,255,255,0.05)",
494
+ background: "rgba(0,0,0,0.14)",
495
+ overflow: "hidden",
496
+ };
497
+
498
+ const summaryStyle: CSSProperties = {
499
+ cursor: "pointer",
500
+ padding: "10px 12px",
501
+ color: "#f0cdd3",
502
+ fontSize: 12,
503
+ fontWeight: 700,
504
+ letterSpacing: "0.04em",
505
+ textTransform: "uppercase",
506
+ };
507
+
508
+ const diagnosticsPreStyle: CSSProperties = {
509
+ margin: 0,
510
+ padding: "0 12px 12px",
511
+ color: "#ffdfe4",
512
+ fontSize: 11,
513
+ lineHeight: 1.55,
514
+ whiteSpace: "pre-wrap",
515
+ wordBreak: "break-word",
516
+ };
517
+
518
+ const emptyTextStyle: CSSProperties = {
519
+ color: "#c99aa3",
520
+ fontSize: 12,
521
+ lineHeight: 1.5,
522
+ };
@@ -0,0 +1,16 @@
1
+ export { explorationEffectsPluginPhaseC as explorationEffectsPlugin } from "./explorationEffectsPluginPhaseC";
2
+ export { legendPlugin } from "./legendPlugin";
3
+ export { neighborhoodPanelPlugin } from "./neighborhoodPanelPlugin";
4
+ export { temporalOverlayPlugin } from "./temporalOverlayPlugin";
5
+ export type {
6
+ GraphPlugin,
7
+ GraphPluginActionRequest,
8
+ GraphPluginContext,
9
+ GraphInspectorState,
10
+ GraphPluginId,
11
+ GraphPluginOverlayDescriptor,
12
+ GraphPluginPanelDescriptor,
13
+ GraphPluginRegistryEntry,
14
+ GraphPluginToolbarItem,
15
+ GraphTemporalState,
16
+ } from "./types";
@@ -0,0 +1,120 @@
1
+ import type { CSSProperties } from "react";
2
+
3
+ import { buildGraphColorLegend } from "../graphColorLegend";
4
+ import type { GraphPlugin } from "./types";
5
+
6
+ const LEGEND_PANEL_ID = "legend-panel";
7
+ const MAX_GROUPS = 8;
8
+
9
+ export const legendPlugin: GraphPlugin = {
10
+ id: "legend",
11
+ mount: () => {},
12
+ unmount: () => {},
13
+ onStateChange: () => {},
14
+ toolbarItems: (context) => [
15
+ {
16
+ id: "legend-toggle",
17
+ label: "Legend",
18
+ title: "Toggle semantic legend",
19
+ active: context.isPanelOpen(LEGEND_PANEL_ID),
20
+ order: 20,
21
+ onClick: () => context.dispatchAction({ type: "togglePanel", panelId: LEGEND_PANEL_ID }),
22
+ },
23
+ ],
24
+ renderPanel: (context) => {
25
+ if (!context.isPanelOpen(LEGEND_PANEL_ID)) {
26
+ return null;
27
+ }
28
+
29
+ const items = buildGraphColorLegend(context.graph, context.theme)
30
+ .sort((left, right) => right.count - left.count)
31
+ .slice(0, MAX_GROUPS);
32
+
33
+ return {
34
+ id: LEGEND_PANEL_ID,
35
+ title: "Legend",
36
+ placement: "bottom",
37
+ order: 10,
38
+ defaultOpen: false,
39
+ preferredWidth: 320,
40
+ preferredHeight: 220,
41
+ content: (
42
+ <div style={panelBodyStyle}>
43
+ <div style={panelEyebrowStyle}>Semantic groups</div>
44
+ {items.length ? (
45
+ <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
46
+ {items.map((item) => (
47
+ <div key={item.id} style={legendRowStyle}>
48
+ <span
49
+ style={{
50
+ ...swatchStyle,
51
+ background: item.color,
52
+ boxShadow: `0 0 0 1px rgba(255,255,255,0.06), 0 0 18px ${item.color}44`,
53
+ }}
54
+ />
55
+ <div style={{ minWidth: 0, flex: 1 }}>
56
+ <div style={rowTitleStyle}>{item.group}</div>
57
+ <div style={rowMetaStyle}>{item.count.toLocaleString()} nodes</div>
58
+ </div>
59
+ </div>
60
+ ))}
61
+ </div>
62
+ ) : (
63
+ <div style={emptyTextStyle}>Legend will populate when the graph metadata is available.</div>
64
+ )}
65
+ </div>
66
+ ),
67
+ };
68
+ },
69
+ };
70
+
71
+ const panelBodyStyle: CSSProperties = {
72
+ display: "flex",
73
+ flexDirection: "column",
74
+ gap: 12,
75
+ };
76
+
77
+ const panelEyebrowStyle: CSSProperties = {
78
+ color: "#c99aa3",
79
+ fontSize: 11,
80
+ fontWeight: 700,
81
+ letterSpacing: "0.08em",
82
+ textTransform: "uppercase",
83
+ };
84
+
85
+ const legendRowStyle: CSSProperties = {
86
+ display: "flex",
87
+ alignItems: "center",
88
+ gap: 10,
89
+ padding: "8px 10px",
90
+ borderRadius: 12,
91
+ border: "1px solid rgba(255,255,255,0.06)",
92
+ background: "rgba(255,255,255,0.025)",
93
+ };
94
+
95
+ const swatchStyle: CSSProperties = {
96
+ width: 10,
97
+ height: 10,
98
+ borderRadius: 999,
99
+ flexShrink: 0,
100
+ };
101
+
102
+ const rowTitleStyle: CSSProperties = {
103
+ color: "#fff1f3",
104
+ fontSize: 13,
105
+ fontWeight: 600,
106
+ overflow: "hidden",
107
+ textOverflow: "ellipsis",
108
+ whiteSpace: "nowrap",
109
+ };
110
+
111
+ const rowMetaStyle: CSSProperties = {
112
+ color: "#c99aa3",
113
+ fontSize: 12,
114
+ };
115
+
116
+ const emptyTextStyle: CSSProperties = {
117
+ color: "#c99aa3",
118
+ fontSize: 12,
119
+ lineHeight: 1.5,
120
+ };