shieldcortex 2.0.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.
- package/LICENSE +21 -0
- package/README.md +282 -0
- package/dashboard/components.json +22 -0
- package/dashboard/eslint.config.mjs +42 -0
- package/dashboard/next.config.ts +7 -0
- package/dashboard/package-lock.json +8053 -0
- package/dashboard/package.json +44 -0
- package/dashboard/postcss.config.mjs +7 -0
- package/dashboard/public/file.svg +1 -0
- package/dashboard/public/globe.svg +1 -0
- package/dashboard/public/next.svg +1 -0
- package/dashboard/public/vercel.svg +1 -0
- package/dashboard/public/window.svg +1 -0
- package/dashboard/scripts/ensure-api.mjs +76 -0
- package/dashboard/src/app/error.tsx +49 -0
- package/dashboard/src/app/favicon.ico +0 -0
- package/dashboard/src/app/globals.css +130 -0
- package/dashboard/src/app/layout.tsx +35 -0
- package/dashboard/src/app/page.tsx +364 -0
- package/dashboard/src/components/Providers.tsx +27 -0
- package/dashboard/src/components/brain/ActivityPulseSystem.tsx +229 -0
- package/dashboard/src/components/brain/BrainMesh.tsx +133 -0
- package/dashboard/src/components/brain/BrainRegions.tsx +254 -0
- package/dashboard/src/components/brain/BrainScene.tsx +255 -0
- package/dashboard/src/components/brain/CategoryLabels.tsx +103 -0
- package/dashboard/src/components/brain/CoreSphere.tsx +215 -0
- package/dashboard/src/components/brain/DataFlowParticles.tsx +123 -0
- package/dashboard/src/components/brain/DataStreamRings.tsx +161 -0
- package/dashboard/src/components/brain/ElectronFlow.tsx +323 -0
- package/dashboard/src/components/brain/HolographicGrid.tsx +235 -0
- package/dashboard/src/components/brain/MemoryLinks.tsx +271 -0
- package/dashboard/src/components/brain/MemoryNode.tsx +245 -0
- package/dashboard/src/components/brain/NeuralPathways.tsx +441 -0
- package/dashboard/src/components/brain/SynapseNodes.tsx +312 -0
- package/dashboard/src/components/brain/TimelineControls.tsx +205 -0
- package/dashboard/src/components/chip/ChipScene.tsx +497 -0
- package/dashboard/src/components/chip/ChipSubstrate.tsx +238 -0
- package/dashboard/src/components/chip/CortexCore.tsx +210 -0
- package/dashboard/src/components/chip/DataBus.tsx +416 -0
- package/dashboard/src/components/chip/MemoryCell.tsx +225 -0
- package/dashboard/src/components/chip/MemoryGrid.tsx +328 -0
- package/dashboard/src/components/chip/QuantumCell.tsx +316 -0
- package/dashboard/src/components/chip/SectionLabel.tsx +113 -0
- package/dashboard/src/components/chip/index.ts +14 -0
- package/dashboard/src/components/controls/ControlPanel.tsx +106 -0
- package/dashboard/src/components/controls/VersionPanel.tsx +185 -0
- package/dashboard/src/components/dashboard/StatsPanel.tsx +164 -0
- package/dashboard/src/components/debug/ActivityLog.tsx +250 -0
- package/dashboard/src/components/debug/DebugPanel.tsx +101 -0
- package/dashboard/src/components/debug/QueryTester.tsx +192 -0
- package/dashboard/src/components/debug/RelationshipGraph.tsx +403 -0
- package/dashboard/src/components/debug/SqlConsole.tsx +319 -0
- package/dashboard/src/components/graph/KnowledgeGraph.tsx +230 -0
- package/dashboard/src/components/graph/OntologyGraph.tsx +631 -0
- package/dashboard/src/components/insights/ActivityHeatmap.tsx +131 -0
- package/dashboard/src/components/insights/InsightsView.tsx +46 -0
- package/dashboard/src/components/insights/KnowledgeMapPanel.tsx +80 -0
- package/dashboard/src/components/insights/QualityPanel.tsx +116 -0
- package/dashboard/src/components/memories/MemoriesView.tsx +150 -0
- package/dashboard/src/components/memories/MemoryCard.tsx +103 -0
- package/dashboard/src/components/memory/MemoryDetail.tsx +325 -0
- package/dashboard/src/components/nav/NavRail.tsx +54 -0
- package/dashboard/src/components/ui/button.tsx +62 -0
- package/dashboard/src/components/ui/card.tsx +92 -0
- package/dashboard/src/components/ui/input.tsx +21 -0
- package/dashboard/src/hooks/useDebouncedValue.ts +24 -0
- package/dashboard/src/hooks/useMemories.ts +458 -0
- package/dashboard/src/hooks/useSuggestions.ts +46 -0
- package/dashboard/src/lib/category-colors.ts +84 -0
- package/dashboard/src/lib/position-algorithm.ts +177 -0
- package/dashboard/src/lib/simplex-noise.ts +217 -0
- package/dashboard/src/lib/store.ts +88 -0
- package/dashboard/src/lib/utils.ts +6 -0
- package/dashboard/src/lib/websocket.ts +249 -0
- package/dashboard/src/types/memory.ts +73 -0
- package/dashboard/tsconfig.json +34 -0
- package/dist/__tests__/consolidation-merge.test.d.ts +9 -0
- package/dist/__tests__/consolidation-merge.test.d.ts.map +1 -0
- package/dist/__tests__/consolidation-merge.test.js +137 -0
- package/dist/__tests__/consolidation-merge.test.js.map +1 -0
- package/dist/__tests__/contradictions.test.d.ts +8 -0
- package/dist/__tests__/contradictions.test.d.ts.map +1 -0
- package/dist/__tests__/contradictions.test.js +78 -0
- package/dist/__tests__/contradictions.test.js.map +1 -0
- package/dist/__tests__/salience-evolution.test.d.ts +7 -0
- package/dist/__tests__/salience-evolution.test.d.ts.map +1 -0
- package/dist/__tests__/salience-evolution.test.js +151 -0
- package/dist/__tests__/salience-evolution.test.js.map +1 -0
- package/dist/__tests__/store.test.d.ts +7 -0
- package/dist/__tests__/store.test.d.ts.map +1 -0
- package/dist/__tests__/store.test.js +582 -0
- package/dist/__tests__/store.test.js.map +1 -0
- package/dist/api/control.d.ts +27 -0
- package/dist/api/control.d.ts.map +1 -0
- package/dist/api/control.js +60 -0
- package/dist/api/control.js.map +1 -0
- package/dist/api/events.d.ts +159 -0
- package/dist/api/events.d.ts.map +1 -0
- package/dist/api/events.js +155 -0
- package/dist/api/events.js.map +1 -0
- package/dist/api/version.d.ts +36 -0
- package/dist/api/version.d.ts.map +1 -0
- package/dist/api/version.js +146 -0
- package/dist/api/version.js.map +1 -0
- package/dist/api/visualization-server.d.ts +11 -0
- package/dist/api/visualization-server.d.ts.map +1 -0
- package/dist/api/visualization-server.js +1186 -0
- package/dist/api/visualization-server.js.map +1 -0
- package/dist/context/project-context.d.ts +57 -0
- package/dist/context/project-context.d.ts.map +1 -0
- package/dist/context/project-context.js +135 -0
- package/dist/context/project-context.js.map +1 -0
- package/dist/database/init.d.ts +49 -0
- package/dist/database/init.d.ts.map +1 -0
- package/dist/database/init.js +567 -0
- package/dist/database/init.js.map +1 -0
- package/dist/defence/__tests__/firewall.test.d.ts +8 -0
- package/dist/defence/__tests__/firewall.test.d.ts.map +1 -0
- package/dist/defence/__tests__/firewall.test.js +123 -0
- package/dist/defence/__tests__/firewall.test.js.map +1 -0
- package/dist/defence/__tests__/fragmentation.test.d.ts +7 -0
- package/dist/defence/__tests__/fragmentation.test.d.ts.map +1 -0
- package/dist/defence/__tests__/fragmentation.test.js +51 -0
- package/dist/defence/__tests__/fragmentation.test.js.map +1 -0
- package/dist/defence/__tests__/pipeline.test.d.ts +8 -0
- package/dist/defence/__tests__/pipeline.test.d.ts.map +1 -0
- package/dist/defence/__tests__/pipeline.test.js +61 -0
- package/dist/defence/__tests__/pipeline.test.js.map +1 -0
- package/dist/defence/__tests__/sensitivity.test.d.ts +7 -0
- package/dist/defence/__tests__/sensitivity.test.d.ts.map +1 -0
- package/dist/defence/__tests__/sensitivity.test.js +61 -0
- package/dist/defence/__tests__/sensitivity.test.js.map +1 -0
- package/dist/defence/__tests__/trust.test.d.ts +7 -0
- package/dist/defence/__tests__/trust.test.d.ts.map +1 -0
- package/dist/defence/__tests__/trust.test.js +49 -0
- package/dist/defence/__tests__/trust.test.js.map +1 -0
- package/dist/defence/audit/index.d.ts +4 -0
- package/dist/defence/audit/index.d.ts.map +1 -0
- package/dist/defence/audit/index.js +3 -0
- package/dist/defence/audit/index.js.map +1 -0
- package/dist/defence/audit/logger.d.ts +14 -0
- package/dist/defence/audit/logger.d.ts.map +1 -0
- package/dist/defence/audit/logger.js +54 -0
- package/dist/defence/audit/logger.js.map +1 -0
- package/dist/defence/audit/queries.d.ts +33 -0
- package/dist/defence/audit/queries.d.ts.map +1 -0
- package/dist/defence/audit/queries.js +103 -0
- package/dist/defence/audit/queries.js.map +1 -0
- package/dist/defence/firewall/anomaly-scorer.d.ts +8 -0
- package/dist/defence/firewall/anomaly-scorer.d.ts.map +1 -0
- package/dist/defence/firewall/anomaly-scorer.js +58 -0
- package/dist/defence/firewall/anomaly-scorer.js.map +1 -0
- package/dist/defence/firewall/encoding-detector.d.ts +13 -0
- package/dist/defence/firewall/encoding-detector.d.ts.map +1 -0
- package/dist/defence/firewall/encoding-detector.js +120 -0
- package/dist/defence/firewall/encoding-detector.js.map +1 -0
- package/dist/defence/firewall/index.d.ts +21 -0
- package/dist/defence/firewall/index.d.ts.map +1 -0
- package/dist/defence/firewall/index.js +133 -0
- package/dist/defence/firewall/index.js.map +1 -0
- package/dist/defence/firewall/instruction-detector.d.ts +12 -0
- package/dist/defence/firewall/instruction-detector.d.ts.map +1 -0
- package/dist/defence/firewall/instruction-detector.js +99 -0
- package/dist/defence/firewall/instruction-detector.js.map +1 -0
- package/dist/defence/firewall/privilege-detector.d.ts +13 -0
- package/dist/defence/firewall/privilege-detector.d.ts.map +1 -0
- package/dist/defence/firewall/privilege-detector.js +89 -0
- package/dist/defence/firewall/privilege-detector.js.map +1 -0
- package/dist/defence/fragmentation/assembly-detector.d.ts +18 -0
- package/dist/defence/fragmentation/assembly-detector.d.ts.map +1 -0
- package/dist/defence/fragmentation/assembly-detector.js +72 -0
- package/dist/defence/fragmentation/assembly-detector.js.map +1 -0
- package/dist/defence/fragmentation/entity-extractor.d.ts +19 -0
- package/dist/defence/fragmentation/entity-extractor.d.ts.map +1 -0
- package/dist/defence/fragmentation/entity-extractor.js +86 -0
- package/dist/defence/fragmentation/entity-extractor.js.map +1 -0
- package/dist/defence/fragmentation/index.d.ts +23 -0
- package/dist/defence/fragmentation/index.d.ts.map +1 -0
- package/dist/defence/fragmentation/index.js +49 -0
- package/dist/defence/fragmentation/index.js.map +1 -0
- package/dist/defence/fragmentation/temporal-analyzer.d.ts +28 -0
- package/dist/defence/fragmentation/temporal-analyzer.d.ts.map +1 -0
- package/dist/defence/fragmentation/temporal-analyzer.js +41 -0
- package/dist/defence/fragmentation/temporal-analyzer.js.map +1 -0
- package/dist/defence/index.d.ts +12 -0
- package/dist/defence/index.d.ts.map +1 -0
- package/dist/defence/index.js +18 -0
- package/dist/defence/index.js.map +1 -0
- package/dist/defence/pipeline.d.ts +9 -0
- package/dist/defence/pipeline.d.ts.map +1 -0
- package/dist/defence/pipeline.js +115 -0
- package/dist/defence/pipeline.js.map +1 -0
- package/dist/defence/scanner/index.d.ts +5 -0
- package/dist/defence/scanner/index.d.ts.map +1 -0
- package/dist/defence/scanner/index.js +5 -0
- package/dist/defence/scanner/index.js.map +1 -0
- package/dist/defence/scanner/scan-existing.d.ts +34 -0
- package/dist/defence/scanner/scan-existing.d.ts.map +1 -0
- package/dist/defence/scanner/scan-existing.js +136 -0
- package/dist/defence/scanner/scan-existing.js.map +1 -0
- package/dist/defence/sensitivity/classifier.d.ts +6 -0
- package/dist/defence/sensitivity/classifier.d.ts.map +1 -0
- package/dist/defence/sensitivity/classifier.js +50 -0
- package/dist/defence/sensitivity/classifier.js.map +1 -0
- package/dist/defence/sensitivity/index.d.ts +11 -0
- package/dist/defence/sensitivity/index.d.ts.map +1 -0
- package/dist/defence/sensitivity/index.js +13 -0
- package/dist/defence/sensitivity/index.js.map +1 -0
- package/dist/defence/sensitivity/patterns.d.ts +14 -0
- package/dist/defence/sensitivity/patterns.d.ts.map +1 -0
- package/dist/defence/sensitivity/patterns.js +67 -0
- package/dist/defence/sensitivity/patterns.js.map +1 -0
- package/dist/defence/sensitivity/redaction.d.ts +17 -0
- package/dist/defence/sensitivity/redaction.d.ts.map +1 -0
- package/dist/defence/sensitivity/redaction.js +47 -0
- package/dist/defence/sensitivity/redaction.js.map +1 -0
- package/dist/defence/trust/index.d.ts +3 -0
- package/dist/defence/trust/index.d.ts.map +1 -0
- package/dist/defence/trust/index.js +3 -0
- package/dist/defence/trust/index.js.map +1 -0
- package/dist/defence/trust/recall-filter.d.ts +10 -0
- package/dist/defence/trust/recall-filter.d.ts.map +1 -0
- package/dist/defence/trust/recall-filter.js +38 -0
- package/dist/defence/trust/recall-filter.js.map +1 -0
- package/dist/defence/trust/source-scorer.d.ts +6 -0
- package/dist/defence/trust/source-scorer.d.ts.map +1 -0
- package/dist/defence/trust/source-scorer.js +34 -0
- package/dist/defence/trust/source-scorer.js.map +1 -0
- package/dist/defence/types.d.ts +88 -0
- package/dist/defence/types.d.ts.map +1 -0
- package/dist/defence/types.js +15 -0
- package/dist/defence/types.js.map +1 -0
- package/dist/embeddings/generator.d.ts +20 -0
- package/dist/embeddings/generator.d.ts.map +1 -0
- package/dist/embeddings/generator.js +83 -0
- package/dist/embeddings/generator.js.map +1 -0
- package/dist/embeddings/index.d.ts +2 -0
- package/dist/embeddings/index.d.ts.map +1 -0
- package/dist/embeddings/index.js +2 -0
- package/dist/embeddings/index.js.map +1 -0
- package/dist/errors.d.ts +74 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +131 -0
- package/dist/errors.js.map +1 -0
- package/dist/graph/backfill.d.ts +6 -0
- package/dist/graph/backfill.d.ts.map +1 -0
- package/dist/graph/backfill.js +33 -0
- package/dist/graph/backfill.js.map +1 -0
- package/dist/graph/extract.d.ts +21 -0
- package/dist/graph/extract.d.ts.map +1 -0
- package/dist/graph/extract.js +231 -0
- package/dist/graph/extract.js.map +1 -0
- package/dist/graph/resolve.d.ts +6 -0
- package/dist/graph/resolve.d.ts.map +1 -0
- package/dist/graph/resolve.js +126 -0
- package/dist/graph/resolve.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +248 -0
- package/dist/index.js.map +1 -0
- package/dist/memory/activation.d.ts +69 -0
- package/dist/memory/activation.d.ts.map +1 -0
- package/dist/memory/activation.js +168 -0
- package/dist/memory/activation.js.map +1 -0
- package/dist/memory/consolidate.d.ts +98 -0
- package/dist/memory/consolidate.d.ts.map +1 -0
- package/dist/memory/consolidate.js +511 -0
- package/dist/memory/consolidate.js.map +1 -0
- package/dist/memory/contradiction.d.ts +69 -0
- package/dist/memory/contradiction.d.ts.map +1 -0
- package/dist/memory/contradiction.js +286 -0
- package/dist/memory/contradiction.js.map +1 -0
- package/dist/memory/decay.d.ts +62 -0
- package/dist/memory/decay.d.ts.map +1 -0
- package/dist/memory/decay.js +184 -0
- package/dist/memory/decay.js.map +1 -0
- package/dist/memory/salience.d.ts +36 -0
- package/dist/memory/salience.d.ts.map +1 -0
- package/dist/memory/salience.js +216 -0
- package/dist/memory/salience.js.map +1 -0
- package/dist/memory/similarity.d.ts +57 -0
- package/dist/memory/similarity.d.ts.map +1 -0
- package/dist/memory/similarity.js +114 -0
- package/dist/memory/similarity.js.map +1 -0
- package/dist/memory/store.d.ts +179 -0
- package/dist/memory/store.d.ts.map +1 -0
- package/dist/memory/store.js +1184 -0
- package/dist/memory/store.js.map +1 -0
- package/dist/memory/types.d.ts +97 -0
- package/dist/memory/types.d.ts.map +1 -0
- package/dist/memory/types.js +30 -0
- package/dist/memory/types.js.map +1 -0
- package/dist/server.d.ts +12 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +568 -0
- package/dist/server.js.map +1 -0
- package/dist/service/install.d.ts +15 -0
- package/dist/service/install.d.ts.map +1 -0
- package/dist/service/install.js +178 -0
- package/dist/service/install.js.map +1 -0
- package/dist/service/templates.d.ts +13 -0
- package/dist/service/templates.d.ts.map +1 -0
- package/dist/service/templates.js +58 -0
- package/dist/service/templates.js.map +1 -0
- package/dist/setup/claude-md.d.ts +12 -0
- package/dist/setup/claude-md.d.ts.map +1 -0
- package/dist/setup/claude-md.js +68 -0
- package/dist/setup/claude-md.js.map +1 -0
- package/dist/setup/clawdbot.d.ts +15 -0
- package/dist/setup/clawdbot.d.ts.map +1 -0
- package/dist/setup/clawdbot.js +118 -0
- package/dist/setup/clawdbot.js.map +1 -0
- package/dist/setup/doctor.d.ts +5 -0
- package/dist/setup/doctor.d.ts.map +1 -0
- package/dist/setup/doctor.js +141 -0
- package/dist/setup/doctor.js.map +1 -0
- package/dist/setup/hooks.d.ts +6 -0
- package/dist/setup/hooks.d.ts.map +1 -0
- package/dist/setup/hooks.js +36 -0
- package/dist/setup/hooks.js.map +1 -0
- package/dist/setup/migrate.d.ts +16 -0
- package/dist/setup/migrate.d.ts.map +1 -0
- package/dist/setup/migrate.js +164 -0
- package/dist/setup/migrate.js.map +1 -0
- package/dist/setup/settings-hooks.d.ts +7 -0
- package/dist/setup/settings-hooks.d.ts.map +1 -0
- package/dist/setup/settings-hooks.js +83 -0
- package/dist/setup/settings-hooks.js.map +1 -0
- package/dist/setup/uninstall.d.ts +12 -0
- package/dist/setup/uninstall.d.ts.map +1 -0
- package/dist/setup/uninstall.js +125 -0
- package/dist/setup/uninstall.js.map +1 -0
- package/dist/tools/context.d.ts +135 -0
- package/dist/tools/context.d.ts.map +1 -0
- package/dist/tools/context.js +273 -0
- package/dist/tools/context.js.map +1 -0
- package/dist/tools/forget.d.ts +53 -0
- package/dist/tools/forget.d.ts.map +1 -0
- package/dist/tools/forget.js +179 -0
- package/dist/tools/forget.js.map +1 -0
- package/dist/tools/graph.d.ts +46 -0
- package/dist/tools/graph.d.ts.map +1 -0
- package/dist/tools/graph.js +206 -0
- package/dist/tools/graph.js.map +1 -0
- package/dist/tools/recall.d.ts +79 -0
- package/dist/tools/recall.d.ts.map +1 -0
- package/dist/tools/recall.js +156 -0
- package/dist/tools/recall.js.map +1 -0
- package/dist/tools/remember.d.ts +83 -0
- package/dist/tools/remember.d.ts.map +1 -0
- package/dist/tools/remember.js +151 -0
- package/dist/tools/remember.js.map +1 -0
- package/dist/worker/brain-worker.d.ts +100 -0
- package/dist/worker/brain-worker.d.ts.map +1 -0
- package/dist/worker/brain-worker.js +283 -0
- package/dist/worker/brain-worker.js.map +1 -0
- package/dist/worker/link-discovery.d.ts +47 -0
- package/dist/worker/link-discovery.d.ts.map +1 -0
- package/dist/worker/link-discovery.js +103 -0
- package/dist/worker/link-discovery.js.map +1 -0
- package/dist/worker/predictive-consolidation.d.ts +46 -0
- package/dist/worker/predictive-consolidation.d.ts.map +1 -0
- package/dist/worker/predictive-consolidation.js +110 -0
- package/dist/worker/predictive-consolidation.js.map +1 -0
- package/dist/worker/types.d.ts +91 -0
- package/dist/worker/types.d.ts.map +1 -0
- package/dist/worker/types.js +22 -0
- package/dist/worker/types.js.map +1 -0
- package/hooks/clawdbot/cortex-memory/HOOK.md +71 -0
- package/hooks/clawdbot/cortex-memory/handler.js +279 -0
- package/package.json +73 -0
- package/scripts/pre-compact-hook.mjs +716 -0
- package/scripts/session-end-hook.mjs +548 -0
- package/scripts/session-start-hook.mjs +221 -0
- package/scripts/start-dashboard.sh +41 -0
- package/scripts/stop-dashboard.sh +21 -0
- package/scripts/stop-hook.mjs +163 -0
|
@@ -0,0 +1,631 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
|
+
import dynamic from 'next/dynamic';
|
|
5
|
+
import { Search, X } from 'lucide-react';
|
|
6
|
+
|
|
7
|
+
const ForceGraph2D = dynamic(() => import('react-force-graph-2d'), { ssr: false });
|
|
8
|
+
|
|
9
|
+
const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001';
|
|
10
|
+
|
|
11
|
+
// Entity type color map
|
|
12
|
+
const ENTITY_COLORS: Record<string, string> = {
|
|
13
|
+
tool: '#22d3ee',
|
|
14
|
+
person: '#34d399',
|
|
15
|
+
concept: '#f59e0b',
|
|
16
|
+
language: '#a78bfa',
|
|
17
|
+
file: '#64748b',
|
|
18
|
+
service: '#f472b6',
|
|
19
|
+
pattern: '#fb923c',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const DEFAULT_COLOR = '#94a3b8';
|
|
23
|
+
|
|
24
|
+
// Predicate colors for edges
|
|
25
|
+
const PREDICATE_COLORS: Record<string, string> = {
|
|
26
|
+
uses: '#22d3ee',
|
|
27
|
+
implements: '#34d399',
|
|
28
|
+
depends_on: '#f59e0b',
|
|
29
|
+
related_to: '#94a3b8',
|
|
30
|
+
part_of: '#a78bfa',
|
|
31
|
+
created_by: '#f472b6',
|
|
32
|
+
extends: '#fb923c',
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const DEFAULT_EDGE_COLOR = '#475569';
|
|
36
|
+
|
|
37
|
+
interface Entity {
|
|
38
|
+
id: number;
|
|
39
|
+
name: string;
|
|
40
|
+
type: string;
|
|
41
|
+
memoryCount: number;
|
|
42
|
+
aliases: string[];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface Triple {
|
|
46
|
+
id: number;
|
|
47
|
+
subject_id: number;
|
|
48
|
+
object_id: number;
|
|
49
|
+
predicate: string;
|
|
50
|
+
subject_name: string;
|
|
51
|
+
subject_type: string;
|
|
52
|
+
object_name: string;
|
|
53
|
+
object_type: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface GraphNode {
|
|
57
|
+
id: number;
|
|
58
|
+
name: string;
|
|
59
|
+
entityType: string;
|
|
60
|
+
memoryCount: number;
|
|
61
|
+
val: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
interface GraphLink {
|
|
65
|
+
source: number;
|
|
66
|
+
target: number;
|
|
67
|
+
predicate: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
interface GraphData {
|
|
71
|
+
nodes: GraphNode[];
|
|
72
|
+
links: GraphLink[];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
interface LinkedMemory {
|
|
76
|
+
id: number;
|
|
77
|
+
title: string;
|
|
78
|
+
type: string;
|
|
79
|
+
category: string;
|
|
80
|
+
salience: number;
|
|
81
|
+
created_at: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
interface EntityDetail {
|
|
85
|
+
entity: Entity;
|
|
86
|
+
triples: Triple[];
|
|
87
|
+
memories: LinkedMemory[];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const ALL_TYPES = ['tool', 'person', 'concept', 'language', 'file', 'service', 'pattern'];
|
|
91
|
+
|
|
92
|
+
export default function OntologyGraph() {
|
|
93
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
94
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
95
|
+
const graphRef = useRef<any>(null);
|
|
96
|
+
const [dimensions, setDimensions] = useState({ width: 800, height: 600 });
|
|
97
|
+
const [entities, setEntities] = useState<Entity[]>([]);
|
|
98
|
+
const [triples, setTriples] = useState<Triple[]>([]);
|
|
99
|
+
const [loading, setLoading] = useState(true);
|
|
100
|
+
const [error, setError] = useState<string | null>(null);
|
|
101
|
+
const [selectedEntity, setSelectedEntity] = useState<EntityDetail | null>(null);
|
|
102
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
103
|
+
const [activeTypes, setActiveTypes] = useState<Set<string>>(new Set(ALL_TYPES));
|
|
104
|
+
|
|
105
|
+
// Resize observer
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
const el = containerRef.current;
|
|
108
|
+
if (!el) return;
|
|
109
|
+
const update = () => setDimensions({ width: el.clientWidth, height: el.clientHeight });
|
|
110
|
+
update();
|
|
111
|
+
const observer = new ResizeObserver(update);
|
|
112
|
+
observer.observe(el);
|
|
113
|
+
return () => observer.disconnect();
|
|
114
|
+
}, []);
|
|
115
|
+
|
|
116
|
+
// Fetch data
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
let cancelled = false;
|
|
119
|
+
|
|
120
|
+
async function fetchData() {
|
|
121
|
+
setLoading(true);
|
|
122
|
+
setError(null);
|
|
123
|
+
try {
|
|
124
|
+
const [entRes, triRes] = await Promise.all([
|
|
125
|
+
fetch(`${API_BASE}/api/graph/entities?limit=500`).then(r => r.json()),
|
|
126
|
+
fetch(`${API_BASE}/api/graph/triples?limit=500`).then(r => r.json()),
|
|
127
|
+
]);
|
|
128
|
+
if (cancelled) return;
|
|
129
|
+
setEntities(entRes.entities || []);
|
|
130
|
+
setTriples(triRes.triples || []);
|
|
131
|
+
setLoading(false);
|
|
132
|
+
} catch (err) {
|
|
133
|
+
if (cancelled) return;
|
|
134
|
+
setError((err as Error).message);
|
|
135
|
+
setLoading(false);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
fetchData();
|
|
140
|
+
return () => { cancelled = true; };
|
|
141
|
+
}, []);
|
|
142
|
+
|
|
143
|
+
// Filter entities
|
|
144
|
+
const filteredEntities = useMemo(() => {
|
|
145
|
+
return entities.filter(e => {
|
|
146
|
+
if (!activeTypes.has(e.type)) return false;
|
|
147
|
+
if (searchQuery && !e.name.toLowerCase().includes(searchQuery.toLowerCase())) return false;
|
|
148
|
+
return true;
|
|
149
|
+
});
|
|
150
|
+
}, [entities, activeTypes, searchQuery]);
|
|
151
|
+
|
|
152
|
+
// Build graph data
|
|
153
|
+
const graphData: GraphData = useMemo(() => {
|
|
154
|
+
const nodeIds = new Set(filteredEntities.map(e => e.id));
|
|
155
|
+
const maxCount = Math.max(1, ...filteredEntities.map(e => e.memoryCount));
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
nodes: filteredEntities.map(e => ({
|
|
159
|
+
id: e.id,
|
|
160
|
+
name: e.name,
|
|
161
|
+
entityType: e.type,
|
|
162
|
+
memoryCount: e.memoryCount,
|
|
163
|
+
val: 2 + (e.memoryCount / maxCount) * 10,
|
|
164
|
+
})),
|
|
165
|
+
links: triples
|
|
166
|
+
.filter(t => nodeIds.has(t.subject_id) && nodeIds.has(t.object_id))
|
|
167
|
+
.map(t => ({
|
|
168
|
+
source: t.subject_id,
|
|
169
|
+
target: t.object_id,
|
|
170
|
+
predicate: t.predicate,
|
|
171
|
+
})),
|
|
172
|
+
};
|
|
173
|
+
}, [filteredEntities, triples]);
|
|
174
|
+
|
|
175
|
+
// Computed stats
|
|
176
|
+
const typeBreakdown = useMemo(() => {
|
|
177
|
+
const counts: Record<string, number> = {};
|
|
178
|
+
for (const e of entities) {
|
|
179
|
+
counts[e.type] = (counts[e.type] || 0) + 1;
|
|
180
|
+
}
|
|
181
|
+
return ALL_TYPES
|
|
182
|
+
.map(t => ({ type: t, count: counts[t] || 0 }))
|
|
183
|
+
.filter(t => t.count > 0)
|
|
184
|
+
.sort((a, b) => b.count - a.count);
|
|
185
|
+
}, [entities]);
|
|
186
|
+
|
|
187
|
+
const topEntities = useMemo(() => {
|
|
188
|
+
return [...entities]
|
|
189
|
+
.sort((a, b) => b.memoryCount - a.memoryCount)
|
|
190
|
+
.slice(0, 15);
|
|
191
|
+
}, [entities]);
|
|
192
|
+
|
|
193
|
+
const predicateBreakdown = useMemo(() => {
|
|
194
|
+
const counts: Record<string, number> = {};
|
|
195
|
+
for (const t of triples) {
|
|
196
|
+
counts[t.predicate] = (counts[t.predicate] || 0) + 1;
|
|
197
|
+
}
|
|
198
|
+
return Object.entries(counts)
|
|
199
|
+
.map(([predicate, count]) => ({ predicate, count }))
|
|
200
|
+
.sort((a, b) => b.count - a.count);
|
|
201
|
+
}, [triples]);
|
|
202
|
+
|
|
203
|
+
const graphStats = useMemo(() => {
|
|
204
|
+
const typesPresent = new Set(entities.map(e => e.type)).size;
|
|
205
|
+
const connectionCounts: Record<number, number> = {};
|
|
206
|
+
for (const t of triples) {
|
|
207
|
+
connectionCounts[t.subject_id] = (connectionCounts[t.subject_id] || 0) + 1;
|
|
208
|
+
connectionCounts[t.object_id] = (connectionCounts[t.object_id] || 0) + 1;
|
|
209
|
+
}
|
|
210
|
+
const connValues = Object.values(connectionCounts);
|
|
211
|
+
const avgConnections = connValues.length > 0
|
|
212
|
+
? (connValues.reduce((a, b) => a + b, 0) / connValues.length).toFixed(1)
|
|
213
|
+
: '0';
|
|
214
|
+
let mostConnected: Entity | null = null;
|
|
215
|
+
let maxConn = 0;
|
|
216
|
+
for (const [idStr, count] of Object.entries(connectionCounts)) {
|
|
217
|
+
if (count > maxConn) {
|
|
218
|
+
maxConn = count;
|
|
219
|
+
mostConnected = entities.find(e => e.id === Number(idStr)) || null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return { typesPresent, avgConnections, mostConnected, maxConn };
|
|
223
|
+
}, [entities, triples]);
|
|
224
|
+
|
|
225
|
+
// Fetch entity detail (triples + memories)
|
|
226
|
+
const fetchEntityDetail = useCallback((entity: Entity) => {
|
|
227
|
+
Promise.all([
|
|
228
|
+
fetch(`${API_BASE}/api/graph/entities/${entity.id}/triples`).then(r => r.json()),
|
|
229
|
+
fetch(`${API_BASE}/api/graph/entities/${entity.id}/memories`).then(r => r.json()),
|
|
230
|
+
])
|
|
231
|
+
.then(([triData, memData]) => {
|
|
232
|
+
setSelectedEntity({
|
|
233
|
+
entity,
|
|
234
|
+
triples: triData.triples || [],
|
|
235
|
+
memories: memData.memories || [],
|
|
236
|
+
});
|
|
237
|
+
})
|
|
238
|
+
.catch(() => {});
|
|
239
|
+
}, []);
|
|
240
|
+
|
|
241
|
+
// Click node -> fetch detail
|
|
242
|
+
const handleNodeClick = useCallback((node: GraphNode) => {
|
|
243
|
+
const entity = entities.find(e => e.id === node.id);
|
|
244
|
+
if (entity) fetchEntityDetail(entity);
|
|
245
|
+
}, [entities, fetchEntityDetail]);
|
|
246
|
+
|
|
247
|
+
const handleEntityClick = useCallback((entity: Entity) => {
|
|
248
|
+
fetchEntityDetail(entity);
|
|
249
|
+
}, [fetchEntityDetail]);
|
|
250
|
+
|
|
251
|
+
const toggleType = (type: string) => {
|
|
252
|
+
setActiveTypes(prev => {
|
|
253
|
+
const next = new Set(prev);
|
|
254
|
+
if (next.has(type)) next.delete(type);
|
|
255
|
+
else next.add(type);
|
|
256
|
+
return next;
|
|
257
|
+
});
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
// Canvas rendering
|
|
261
|
+
const nodeCanvasObject = useCallback(
|
|
262
|
+
(node: GraphNode, ctx: CanvasRenderingContext2D, globalScale: number) => {
|
|
263
|
+
const x = (node as unknown as { x: number }).x;
|
|
264
|
+
const y = (node as unknown as { y: number }).y;
|
|
265
|
+
if (x == null || y == null) return;
|
|
266
|
+
|
|
267
|
+
const radius = Math.max(3, node.val);
|
|
268
|
+
const color = ENTITY_COLORS[node.entityType] || DEFAULT_COLOR;
|
|
269
|
+
const isSelected = selectedEntity?.entity.id === node.id;
|
|
270
|
+
|
|
271
|
+
ctx.beginPath();
|
|
272
|
+
ctx.arc(x, y, radius, 0, 2 * Math.PI);
|
|
273
|
+
ctx.fillStyle = color;
|
|
274
|
+
ctx.fill();
|
|
275
|
+
|
|
276
|
+
if (isSelected) {
|
|
277
|
+
ctx.lineWidth = 2;
|
|
278
|
+
ctx.strokeStyle = '#ffffff';
|
|
279
|
+
ctx.stroke();
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Label when zoomed
|
|
283
|
+
if (globalScale > 3) {
|
|
284
|
+
const maxChars = 30;
|
|
285
|
+
const label = node.name.length > maxChars ? node.name.slice(0, maxChars) + '...' : node.name;
|
|
286
|
+
const fontSize = Math.max(10, 12 / globalScale);
|
|
287
|
+
ctx.font = `${fontSize}px Sans-Serif`;
|
|
288
|
+
ctx.textAlign = 'center';
|
|
289
|
+
ctx.textBaseline = 'top';
|
|
290
|
+
|
|
291
|
+
const textWidth = ctx.measureText(label).width;
|
|
292
|
+
const textY = y + radius + 3;
|
|
293
|
+
const padding = 2;
|
|
294
|
+
|
|
295
|
+
ctx.fillStyle = 'rgba(2, 6, 23, 0.85)';
|
|
296
|
+
ctx.beginPath();
|
|
297
|
+
ctx.roundRect(
|
|
298
|
+
x - textWidth / 2 - padding,
|
|
299
|
+
textY - padding,
|
|
300
|
+
textWidth + padding * 2,
|
|
301
|
+
fontSize + padding * 2,
|
|
302
|
+
3,
|
|
303
|
+
);
|
|
304
|
+
ctx.fill();
|
|
305
|
+
|
|
306
|
+
ctx.fillStyle = '#e2e8f0';
|
|
307
|
+
ctx.fillText(label, x, textY);
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
[selectedEntity],
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
const linkCanvasObject = useCallback(
|
|
314
|
+
(link: GraphLink, ctx: CanvasRenderingContext2D) => {
|
|
315
|
+
const source = link.source as unknown as { x: number; y: number };
|
|
316
|
+
const target = link.target as unknown as { x: number; y: number };
|
|
317
|
+
if (!source?.x || !target?.x) return;
|
|
318
|
+
|
|
319
|
+
ctx.beginPath();
|
|
320
|
+
ctx.moveTo(source.x, source.y);
|
|
321
|
+
ctx.lineTo(target.x, target.y);
|
|
322
|
+
ctx.strokeStyle = PREDICATE_COLORS[link.predicate] || DEFAULT_EDGE_COLOR;
|
|
323
|
+
ctx.lineWidth = 1.5;
|
|
324
|
+
ctx.stroke();
|
|
325
|
+
},
|
|
326
|
+
[],
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
const linkLabel = useCallback((link: GraphLink) => link.predicate, []);
|
|
330
|
+
|
|
331
|
+
const nodeLabel = useCallback(
|
|
332
|
+
(node: GraphNode) =>
|
|
333
|
+
`${node.name}\nType: ${node.entityType}\nMemories: ${node.memoryCount}`,
|
|
334
|
+
[],
|
|
335
|
+
);
|
|
336
|
+
|
|
337
|
+
// Recent entities for bottom panel (excluding files for readability)
|
|
338
|
+
const recentEntities = useMemo(() => {
|
|
339
|
+
return [...entities]
|
|
340
|
+
.filter(e => e.type !== 'file')
|
|
341
|
+
.sort((a, b) => b.memoryCount - a.memoryCount)
|
|
342
|
+
.slice(0, 30);
|
|
343
|
+
}, [entities]);
|
|
344
|
+
|
|
345
|
+
if (loading) {
|
|
346
|
+
return (
|
|
347
|
+
<div className="w-full h-full flex items-center justify-center">
|
|
348
|
+
<div className="text-slate-400 animate-pulse">Loading ontology graph...</div>
|
|
349
|
+
</div>
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (error) {
|
|
354
|
+
return (
|
|
355
|
+
<div className="w-full h-full flex items-center justify-center">
|
|
356
|
+
<div className="text-red-400">Failed to load ontology: {error}</div>
|
|
357
|
+
</div>
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const maxTypeCount = Math.max(1, ...typeBreakdown.map(t => t.count));
|
|
362
|
+
|
|
363
|
+
return (
|
|
364
|
+
<div style={{ width: '100%', height: '100%', display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
|
|
365
|
+
{/* Controls bar */}
|
|
366
|
+
<div className="flex items-center gap-3 px-4 py-2 border-b border-slate-800 bg-slate-900/50 shrink-0">
|
|
367
|
+
{/* Search */}
|
|
368
|
+
<div className="relative">
|
|
369
|
+
<Search size={14} className="absolute left-2 top-1/2 -translate-y-1/2 text-slate-500" />
|
|
370
|
+
<input
|
|
371
|
+
type="text"
|
|
372
|
+
placeholder="Filter entities..."
|
|
373
|
+
value={searchQuery}
|
|
374
|
+
onChange={e => setSearchQuery(e.target.value)}
|
|
375
|
+
className="pl-7 pr-2 py-1 text-sm bg-slate-800 border border-slate-700 rounded text-white placeholder:text-slate-500 w-48 focus:outline-none focus:ring-1 focus:ring-cyan-500"
|
|
376
|
+
/>
|
|
377
|
+
</div>
|
|
378
|
+
|
|
379
|
+
{/* Type filters */}
|
|
380
|
+
<div className="flex items-center gap-1">
|
|
381
|
+
{ALL_TYPES.map(type => (
|
|
382
|
+
<button
|
|
383
|
+
key={type}
|
|
384
|
+
onClick={() => toggleType(type)}
|
|
385
|
+
className={`px-2 py-0.5 text-xs rounded-full border transition-colors ${
|
|
386
|
+
activeTypes.has(type)
|
|
387
|
+
? 'border-transparent text-white'
|
|
388
|
+
: 'border-slate-600 text-slate-500 bg-transparent'
|
|
389
|
+
}`}
|
|
390
|
+
style={activeTypes.has(type) ? { backgroundColor: ENTITY_COLORS[type] + '40', color: ENTITY_COLORS[type] } : {}}
|
|
391
|
+
>
|
|
392
|
+
{type}
|
|
393
|
+
</button>
|
|
394
|
+
))}
|
|
395
|
+
</div>
|
|
396
|
+
|
|
397
|
+
<span className="text-xs text-slate-500 ml-auto">
|
|
398
|
+
{filteredEntities.length} entities, {graphData.links.length} triples
|
|
399
|
+
</span>
|
|
400
|
+
</div>
|
|
401
|
+
|
|
402
|
+
{/* Main content: graph + sidebar */}
|
|
403
|
+
<div style={{ flex: 1, display: 'flex', minHeight: 0, overflow: 'hidden' }}>
|
|
404
|
+
{/* Force graph */}
|
|
405
|
+
<div ref={containerRef} style={{ flex: 3, minHeight: 0, minWidth: 0, position: 'relative' }}>
|
|
406
|
+
{dimensions.width > 0 && (
|
|
407
|
+
<ForceGraph2D
|
|
408
|
+
ref={graphRef as never}
|
|
409
|
+
graphData={graphData}
|
|
410
|
+
width={dimensions.width}
|
|
411
|
+
height={dimensions.height}
|
|
412
|
+
backgroundColor="rgba(0,0,0,0)"
|
|
413
|
+
nodeCanvasObject={nodeCanvasObject as never}
|
|
414
|
+
nodeLabel={nodeLabel as never}
|
|
415
|
+
onNodeClick={handleNodeClick as never}
|
|
416
|
+
linkCanvasObject={linkCanvasObject as never}
|
|
417
|
+
linkLabel={linkLabel as never}
|
|
418
|
+
linkDirectionalArrowLength={4}
|
|
419
|
+
linkDirectionalArrowRelPos={0.9}
|
|
420
|
+
d3AlphaDecay={0.02}
|
|
421
|
+
d3VelocityDecay={0.3}
|
|
422
|
+
warmupTicks={100}
|
|
423
|
+
cooldownTicks={200}
|
|
424
|
+
/>
|
|
425
|
+
)}
|
|
426
|
+
</div>
|
|
427
|
+
|
|
428
|
+
{/* Right panel: detail or stats */}
|
|
429
|
+
<div className="border-l border-slate-800 bg-slate-900/30 overflow-y-auto" style={{ flex: 2, minWidth: 240, maxWidth: 400 }}>
|
|
430
|
+
{selectedEntity ? (
|
|
431
|
+
/* Entity detail */
|
|
432
|
+
<div className="p-4">
|
|
433
|
+
<div className="flex items-center justify-between mb-3">
|
|
434
|
+
<h3 className="text-sm font-semibold text-white truncate">{selectedEntity.entity.name}</h3>
|
|
435
|
+
<button onClick={() => setSelectedEntity(null)} className="text-slate-500 hover:text-white">
|
|
436
|
+
<X size={16} />
|
|
437
|
+
</button>
|
|
438
|
+
</div>
|
|
439
|
+
|
|
440
|
+
<div className="flex items-center gap-2 mb-4">
|
|
441
|
+
<span
|
|
442
|
+
className="text-xs px-2 py-0.5 rounded-full"
|
|
443
|
+
style={{ backgroundColor: (ENTITY_COLORS[selectedEntity.entity.type] || DEFAULT_COLOR) + '30', color: ENTITY_COLORS[selectedEntity.entity.type] || DEFAULT_COLOR }}
|
|
444
|
+
>
|
|
445
|
+
{selectedEntity.entity.type}
|
|
446
|
+
</span>
|
|
447
|
+
<span className="text-xs text-slate-500">{selectedEntity.entity.memoryCount} memories</span>
|
|
448
|
+
</div>
|
|
449
|
+
|
|
450
|
+
{selectedEntity.entity.aliases.length > 0 && (
|
|
451
|
+
<div className="mb-4">
|
|
452
|
+
<div className="text-xs text-slate-400 mb-1">Aliases</div>
|
|
453
|
+
<div className="flex flex-wrap gap-1">
|
|
454
|
+
{selectedEntity.entity.aliases.map((a, i) => (
|
|
455
|
+
<span key={i} className="text-xs bg-slate-800 text-slate-300 px-1.5 py-0.5 rounded">{a}</span>
|
|
456
|
+
))}
|
|
457
|
+
</div>
|
|
458
|
+
</div>
|
|
459
|
+
)}
|
|
460
|
+
|
|
461
|
+
<div className="text-xs text-slate-400 mb-2">Relationships ({selectedEntity.triples.length})</div>
|
|
462
|
+
<div className="space-y-2 mb-4">
|
|
463
|
+
{selectedEntity.triples.map(t => (
|
|
464
|
+
<div key={t.id} className="text-xs bg-slate-800/50 rounded p-2">
|
|
465
|
+
<span className="text-cyan-400">{t.subject_name}</span>
|
|
466
|
+
<span className="text-slate-500 mx-1">{t.predicate}</span>
|
|
467
|
+
<span className="text-amber-400">{t.object_name}</span>
|
|
468
|
+
</div>
|
|
469
|
+
))}
|
|
470
|
+
{selectedEntity.triples.length === 0 && (
|
|
471
|
+
<div className="text-xs text-slate-600">No relationships found</div>
|
|
472
|
+
)}
|
|
473
|
+
</div>
|
|
474
|
+
|
|
475
|
+
<div className="text-xs text-slate-400 mb-2">Linked Memories ({selectedEntity.memories.length})</div>
|
|
476
|
+
<div className="space-y-1.5">
|
|
477
|
+
{selectedEntity.memories.map(m => (
|
|
478
|
+
<div key={m.id} className="text-xs bg-slate-800/50 rounded p-2">
|
|
479
|
+
<div className="text-slate-200 mb-1">{m.title}</div>
|
|
480
|
+
<div className="flex items-center gap-2">
|
|
481
|
+
<span className="text-slate-500">{m.category}</span>
|
|
482
|
+
<span className="text-slate-600">{m.type.replace('_', '-')}</span>
|
|
483
|
+
<span className="text-slate-600 ml-auto">{new Date(m.created_at).toLocaleDateString()}</span>
|
|
484
|
+
</div>
|
|
485
|
+
</div>
|
|
486
|
+
))}
|
|
487
|
+
{selectedEntity.memories.length === 0 && (
|
|
488
|
+
<div className="text-xs text-slate-600">No linked memories</div>
|
|
489
|
+
)}
|
|
490
|
+
</div>
|
|
491
|
+
</div>
|
|
492
|
+
) : (
|
|
493
|
+
/* Stats panels */
|
|
494
|
+
<div className="p-4 space-y-6">
|
|
495
|
+
{/* Graph Stats */}
|
|
496
|
+
<div>
|
|
497
|
+
<h3 className="text-xs font-semibold text-slate-400 uppercase tracking-wider mb-3">Overview</h3>
|
|
498
|
+
<div className="grid grid-cols-2 gap-2">
|
|
499
|
+
<div className="bg-slate-800/50 rounded p-2.5">
|
|
500
|
+
<div className="text-lg font-bold text-white">{entities.length}</div>
|
|
501
|
+
<div className="text-xs text-slate-500">Entities</div>
|
|
502
|
+
</div>
|
|
503
|
+
<div className="bg-slate-800/50 rounded p-2.5">
|
|
504
|
+
<div className="text-lg font-bold text-white">{triples.length}</div>
|
|
505
|
+
<div className="text-xs text-slate-500">Triples</div>
|
|
506
|
+
</div>
|
|
507
|
+
<div className="bg-slate-800/50 rounded p-2.5">
|
|
508
|
+
<div className="text-lg font-bold text-white">{graphStats.typesPresent}</div>
|
|
509
|
+
<div className="text-xs text-slate-500">Types</div>
|
|
510
|
+
</div>
|
|
511
|
+
<div className="bg-slate-800/50 rounded p-2.5">
|
|
512
|
+
<div className="text-lg font-bold text-white">{graphStats.avgConnections}</div>
|
|
513
|
+
<div className="text-xs text-slate-500">Avg connections</div>
|
|
514
|
+
</div>
|
|
515
|
+
</div>
|
|
516
|
+
{graphStats.mostConnected && (
|
|
517
|
+
<div className="mt-2 text-xs text-slate-500">
|
|
518
|
+
Most connected: <span className="text-white">{graphStats.mostConnected.name}</span> ({graphStats.maxConn})
|
|
519
|
+
</div>
|
|
520
|
+
)}
|
|
521
|
+
</div>
|
|
522
|
+
|
|
523
|
+
{/* Entity Type Breakdown */}
|
|
524
|
+
<div>
|
|
525
|
+
<h3 className="text-xs font-semibold text-slate-400 uppercase tracking-wider mb-3">Entity Types</h3>
|
|
526
|
+
<div className="space-y-1.5">
|
|
527
|
+
{typeBreakdown.map(({ type, count }) => (
|
|
528
|
+
<button
|
|
529
|
+
key={type}
|
|
530
|
+
onClick={() => toggleType(type)}
|
|
531
|
+
className="w-full flex items-center gap-2 group"
|
|
532
|
+
>
|
|
533
|
+
<div
|
|
534
|
+
className="w-2.5 h-2.5 rounded-full shrink-0"
|
|
535
|
+
style={{ backgroundColor: ENTITY_COLORS[type] || DEFAULT_COLOR, opacity: activeTypes.has(type) ? 1 : 0.3 }}
|
|
536
|
+
/>
|
|
537
|
+
<span className={`text-xs flex-1 text-left ${activeTypes.has(type) ? 'text-slate-300' : 'text-slate-600'}`}>
|
|
538
|
+
{type}
|
|
539
|
+
</span>
|
|
540
|
+
<div className="flex-1 h-1.5 bg-slate-800 rounded-full overflow-hidden">
|
|
541
|
+
<div
|
|
542
|
+
className="h-full rounded-full transition-all"
|
|
543
|
+
style={{
|
|
544
|
+
width: `${(count / maxTypeCount) * 100}%`,
|
|
545
|
+
backgroundColor: ENTITY_COLORS[type] || DEFAULT_COLOR,
|
|
546
|
+
opacity: activeTypes.has(type) ? 0.7 : 0.2,
|
|
547
|
+
}}
|
|
548
|
+
/>
|
|
549
|
+
</div>
|
|
550
|
+
<span className={`text-xs tabular-nums w-6 text-right ${activeTypes.has(type) ? 'text-slate-400' : 'text-slate-600'}`}>
|
|
551
|
+
{count}
|
|
552
|
+
</span>
|
|
553
|
+
</button>
|
|
554
|
+
))}
|
|
555
|
+
</div>
|
|
556
|
+
</div>
|
|
557
|
+
|
|
558
|
+
{/* Predicate Breakdown */}
|
|
559
|
+
<div>
|
|
560
|
+
<h3 className="text-xs font-semibold text-slate-400 uppercase tracking-wider mb-3">Relationships</h3>
|
|
561
|
+
{predicateBreakdown.length > 0 ? (
|
|
562
|
+
<div className="space-y-1.5">
|
|
563
|
+
{predicateBreakdown.map(({ predicate, count }) => (
|
|
564
|
+
<div key={predicate} className="flex items-center gap-2">
|
|
565
|
+
<div
|
|
566
|
+
className="w-2.5 h-2.5 rounded-full shrink-0"
|
|
567
|
+
style={{ backgroundColor: PREDICATE_COLORS[predicate] || DEFAULT_EDGE_COLOR }}
|
|
568
|
+
/>
|
|
569
|
+
<span className="text-xs text-slate-300 flex-1">{predicate}</span>
|
|
570
|
+
<span className="text-xs text-slate-400 tabular-nums">{count}</span>
|
|
571
|
+
</div>
|
|
572
|
+
))}
|
|
573
|
+
</div>
|
|
574
|
+
) : (
|
|
575
|
+
<div className="text-xs text-slate-600">No relationships extracted yet</div>
|
|
576
|
+
)}
|
|
577
|
+
</div>
|
|
578
|
+
|
|
579
|
+
{/* Top Entities */}
|
|
580
|
+
<div>
|
|
581
|
+
<h3 className="text-xs font-semibold text-slate-400 uppercase tracking-wider mb-3">Top Entities</h3>
|
|
582
|
+
<div className="space-y-1">
|
|
583
|
+
{topEntities.map(entity => (
|
|
584
|
+
<button
|
|
585
|
+
key={entity.id}
|
|
586
|
+
onClick={() => handleEntityClick(entity)}
|
|
587
|
+
className="w-full flex items-center gap-2 py-1 px-1.5 rounded hover:bg-slate-800/50 transition-colors group"
|
|
588
|
+
>
|
|
589
|
+
<div
|
|
590
|
+
className="w-2 h-2 rounded-full shrink-0"
|
|
591
|
+
style={{ backgroundColor: ENTITY_COLORS[entity.type] || DEFAULT_COLOR }}
|
|
592
|
+
/>
|
|
593
|
+
<span className="text-xs text-slate-300 flex-1 text-left truncate group-hover:text-white">
|
|
594
|
+
{entity.name}
|
|
595
|
+
</span>
|
|
596
|
+
<span className="text-xs text-slate-600 tabular-nums shrink-0">
|
|
597
|
+
{entity.memoryCount}
|
|
598
|
+
</span>
|
|
599
|
+
</button>
|
|
600
|
+
))}
|
|
601
|
+
</div>
|
|
602
|
+
</div>
|
|
603
|
+
</div>
|
|
604
|
+
)}
|
|
605
|
+
</div>
|
|
606
|
+
</div>
|
|
607
|
+
|
|
608
|
+
{/* Bottom panel: entity tags */}
|
|
609
|
+
<div className="border-t border-slate-800 bg-slate-900/50 px-4 py-2.5 shrink-0">
|
|
610
|
+
<div className="flex items-center gap-2 overflow-x-auto scrollbar-hide" style={{ scrollbarWidth: 'none' }}>
|
|
611
|
+
<span className="text-xs text-slate-500 shrink-0">Known:</span>
|
|
612
|
+
{recentEntities.map(entity => (
|
|
613
|
+
<button
|
|
614
|
+
key={entity.id}
|
|
615
|
+
onClick={() => handleEntityClick(entity)}
|
|
616
|
+
className="shrink-0 flex items-center gap-1.5 px-2 py-1 rounded-full text-xs transition-colors hover:bg-slate-800"
|
|
617
|
+
style={{ color: ENTITY_COLORS[entity.type] || DEFAULT_COLOR }}
|
|
618
|
+
>
|
|
619
|
+
<div
|
|
620
|
+
className="w-1.5 h-1.5 rounded-full"
|
|
621
|
+
style={{ backgroundColor: ENTITY_COLORS[entity.type] || DEFAULT_COLOR }}
|
|
622
|
+
/>
|
|
623
|
+
{entity.name}
|
|
624
|
+
<span className="text-slate-600">{entity.memoryCount}</span>
|
|
625
|
+
</button>
|
|
626
|
+
))}
|
|
627
|
+
</div>
|
|
628
|
+
</div>
|
|
629
|
+
</div>
|
|
630
|
+
);
|
|
631
|
+
}
|