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,364 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Main Dashboard Page
|
|
5
|
+
* Multi-view dashboard for the ShieldCortex memory system
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { useState, useRef, useEffect } from 'react';
|
|
9
|
+
import { AnimatePresence, motion } from 'framer-motion';
|
|
10
|
+
import dynamic from 'next/dynamic';
|
|
11
|
+
import { useMemoriesWithRealtime, useStats, useAccessMemory, useConsolidate, useProjects, useMemoryLinks, useControlStatus, usePauseMemory, useResumeMemory } from '@/hooks/useMemories';
|
|
12
|
+
import { useDashboardStore } from '@/lib/store';
|
|
13
|
+
import { useDebouncedValue } from '@/hooks/useDebouncedValue';
|
|
14
|
+
import { useSuggestions } from '@/hooks/useSuggestions';
|
|
15
|
+
import { MemoryDetail } from '@/components/memory/MemoryDetail';
|
|
16
|
+
import { MemoriesView } from '@/components/memories/MemoriesView';
|
|
17
|
+
import { NavRail } from '@/components/nav/NavRail';
|
|
18
|
+
import { Button } from '@/components/ui/button';
|
|
19
|
+
import { Input } from '@/components/ui/input';
|
|
20
|
+
import { InsightsView } from '@/components/insights/InsightsView';
|
|
21
|
+
import { Memory } from '@/types/memory';
|
|
22
|
+
|
|
23
|
+
// Dynamic imports (avoid SSR issues with canvas/WebGL)
|
|
24
|
+
const KnowledgeGraph = dynamic(
|
|
25
|
+
() => import('@/components/graph/KnowledgeGraph'),
|
|
26
|
+
{
|
|
27
|
+
ssr: false,
|
|
28
|
+
loading: () => (
|
|
29
|
+
<div className="w-full h-full flex items-center justify-center bg-slate-950">
|
|
30
|
+
<div className="text-slate-400 animate-pulse">Loading Graph...</div>
|
|
31
|
+
</div>
|
|
32
|
+
),
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const OntologyGraph = dynamic(
|
|
37
|
+
() => import('@/components/graph/OntologyGraph'),
|
|
38
|
+
{
|
|
39
|
+
ssr: false,
|
|
40
|
+
loading: () => (
|
|
41
|
+
<div className="w-full h-full flex items-center justify-center bg-slate-950">
|
|
42
|
+
<div className="text-slate-400 animate-pulse">Loading Ontology...</div>
|
|
43
|
+
</div>
|
|
44
|
+
),
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const BrainScene = dynamic(
|
|
49
|
+
() => import('@/components/brain/BrainScene').then((mod) => mod.BrainScene),
|
|
50
|
+
{
|
|
51
|
+
ssr: false,
|
|
52
|
+
loading: () => (
|
|
53
|
+
<div className="w-full h-full flex items-center justify-center bg-slate-950">
|
|
54
|
+
<div className="text-slate-400 animate-pulse">Loading 3D Brain...</div>
|
|
55
|
+
</div>
|
|
56
|
+
),
|
|
57
|
+
}
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
export default function DashboardPage() {
|
|
61
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
62
|
+
const [showSuggestions, setShowSuggestions] = useState(false);
|
|
63
|
+
const [selectedProject, setSelectedProject] = useState<string | undefined>(undefined);
|
|
64
|
+
const [showFilters, setShowFilters] = useState(false);
|
|
65
|
+
const [typeFilter, setTypeFilter] = useState<string | undefined>(undefined);
|
|
66
|
+
const [categoryFilter, setCategoryFilter] = useState<string | undefined>(undefined);
|
|
67
|
+
const searchInputRef = useRef<HTMLInputElement>(null);
|
|
68
|
+
const suggestionsRef = useRef<HTMLDivElement>(null);
|
|
69
|
+
|
|
70
|
+
// Debounce search to avoid API calls on every keystroke
|
|
71
|
+
const debouncedSearch = useDebouncedValue(searchQuery, 300);
|
|
72
|
+
|
|
73
|
+
// Zustand store
|
|
74
|
+
const { viewMode, selectedMemory, setSelectedMemory } = useDashboardStore();
|
|
75
|
+
|
|
76
|
+
// Search suggestions
|
|
77
|
+
const { data: suggestions = [] } = useSuggestions(searchQuery);
|
|
78
|
+
|
|
79
|
+
// Fetch projects for dropdown
|
|
80
|
+
const { data: projectsData } = useProjects();
|
|
81
|
+
|
|
82
|
+
// Close suggestions when clicking outside
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
const handleClickOutside = (event: MouseEvent) => {
|
|
85
|
+
if (
|
|
86
|
+
suggestionsRef.current &&
|
|
87
|
+
!suggestionsRef.current.contains(event.target as Node) &&
|
|
88
|
+
searchInputRef.current &&
|
|
89
|
+
!searchInputRef.current.contains(event.target as Node)
|
|
90
|
+
) {
|
|
91
|
+
setShowSuggestions(false);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
96
|
+
return () => document.removeEventListener('mousedown', handleClickOutside);
|
|
97
|
+
}, []);
|
|
98
|
+
|
|
99
|
+
const handleSelectSuggestion = (text: string) => {
|
|
100
|
+
setSearchQuery(text);
|
|
101
|
+
setShowSuggestions(false);
|
|
102
|
+
searchInputRef.current?.focus();
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// Data fetching with real-time WebSocket updates
|
|
106
|
+
const {
|
|
107
|
+
data: memories = [],
|
|
108
|
+
isLoading: memoriesLoading,
|
|
109
|
+
isConnected,
|
|
110
|
+
} = useMemoriesWithRealtime({
|
|
111
|
+
limit: 200,
|
|
112
|
+
query: debouncedSearch || undefined,
|
|
113
|
+
mode: debouncedSearch ? 'search' : 'recent',
|
|
114
|
+
project: selectedProject,
|
|
115
|
+
type: typeFilter,
|
|
116
|
+
category: categoryFilter,
|
|
117
|
+
});
|
|
118
|
+
const { data: stats, isLoading: _statsLoading } = useStats(selectedProject);
|
|
119
|
+
const { data: links = [] } = useMemoryLinks(selectedProject);
|
|
120
|
+
|
|
121
|
+
// Mutations
|
|
122
|
+
const accessMutation = useAccessMemory();
|
|
123
|
+
const consolidateMutation = useConsolidate();
|
|
124
|
+
|
|
125
|
+
// Control status
|
|
126
|
+
const { data: controlStatus } = useControlStatus();
|
|
127
|
+
const _pauseMutation = usePauseMemory();
|
|
128
|
+
const _resumeMutation = useResumeMemory();
|
|
129
|
+
const isPaused = controlStatus?.paused ?? false;
|
|
130
|
+
|
|
131
|
+
const handleSelectMemory = (memory: Memory | null) => {
|
|
132
|
+
setSelectedMemory(memory);
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const handleSelectMemoryById = (id: number) => {
|
|
136
|
+
const memory = memories.find(m => m.id === id);
|
|
137
|
+
if (memory) {
|
|
138
|
+
setSelectedMemory(memory);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const handleReinforce = (id: number) => {
|
|
143
|
+
accessMutation.mutate(id);
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const _handleConsolidate = () => {
|
|
147
|
+
consolidateMutation.mutate();
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
return (
|
|
151
|
+
<div className="h-screen w-screen bg-slate-950 text-white overflow-hidden flex flex-col">
|
|
152
|
+
{/* Top Bar */}
|
|
153
|
+
<header className="h-14 border-b border-slate-800 flex items-center justify-between px-4 bg-slate-900/50 backdrop-blur-sm shrink-0">
|
|
154
|
+
<div className="flex items-center gap-4">
|
|
155
|
+
<h1 className="text-xl font-bold bg-gradient-to-r from-blue-400 via-purple-500 to-pink-500 bg-clip-text text-transparent">
|
|
156
|
+
🧠 ShieldCortex
|
|
157
|
+
</h1>
|
|
158
|
+
|
|
159
|
+
{/* Project Selector */}
|
|
160
|
+
<select
|
|
161
|
+
value={selectedProject || ''}
|
|
162
|
+
onChange={(e) => setSelectedProject(e.target.value || undefined)}
|
|
163
|
+
className="bg-slate-800 border border-slate-700 text-white text-sm rounded-lg px-3 py-1.5 focus:ring-blue-500 focus:border-blue-500"
|
|
164
|
+
>
|
|
165
|
+
{projectsData?.projects.map((p) => (
|
|
166
|
+
<option key={p.project || 'all'} value={p.project || ''}>
|
|
167
|
+
{p.label} ({p.memory_count})
|
|
168
|
+
</option>
|
|
169
|
+
))}
|
|
170
|
+
</select>
|
|
171
|
+
|
|
172
|
+
{/* Search Input */}
|
|
173
|
+
<div className="relative">
|
|
174
|
+
<Input
|
|
175
|
+
ref={searchInputRef}
|
|
176
|
+
type="text"
|
|
177
|
+
placeholder="Search memories..."
|
|
178
|
+
value={searchQuery}
|
|
179
|
+
onChange={(e) => {
|
|
180
|
+
setSearchQuery(e.target.value);
|
|
181
|
+
setShowSuggestions(true);
|
|
182
|
+
}}
|
|
183
|
+
onFocus={() => setShowSuggestions(true)}
|
|
184
|
+
onKeyDown={(e) => {
|
|
185
|
+
if (e.key === 'Escape') {
|
|
186
|
+
setShowSuggestions(false);
|
|
187
|
+
}
|
|
188
|
+
}}
|
|
189
|
+
className="w-64 bg-slate-800 border-slate-700 text-white placeholder:text-slate-400 focus:ring-blue-500"
|
|
190
|
+
/>
|
|
191
|
+
{/* Suggestions dropdown */}
|
|
192
|
+
{showSuggestions && suggestions.length > 0 && (
|
|
193
|
+
<div
|
|
194
|
+
ref={suggestionsRef}
|
|
195
|
+
className="absolute top-full left-0 right-0 mt-1 bg-slate-800 border border-slate-700 rounded-lg shadow-xl overflow-hidden z-50"
|
|
196
|
+
>
|
|
197
|
+
{suggestions.map((suggestion, index) => (
|
|
198
|
+
<button
|
|
199
|
+
key={`${suggestion.type}-${suggestion.text}-${index}`}
|
|
200
|
+
onClick={() => handleSelectSuggestion(suggestion.text)}
|
|
201
|
+
className="w-full px-3 py-2 text-left hover:bg-slate-700 transition-colors flex items-center gap-2"
|
|
202
|
+
>
|
|
203
|
+
<span className="text-white text-sm truncate flex-1">
|
|
204
|
+
{suggestion.text}
|
|
205
|
+
</span>
|
|
206
|
+
<span className="text-xs px-1.5 py-0.5 rounded bg-slate-600 text-slate-300">
|
|
207
|
+
{suggestion.type}
|
|
208
|
+
</span>
|
|
209
|
+
</button>
|
|
210
|
+
))}
|
|
211
|
+
</div>
|
|
212
|
+
)}
|
|
213
|
+
</div>
|
|
214
|
+
|
|
215
|
+
{/* Filter Toggle */}
|
|
216
|
+
<Button
|
|
217
|
+
variant="outline"
|
|
218
|
+
size="sm"
|
|
219
|
+
onClick={() => setShowFilters(!showFilters)}
|
|
220
|
+
className={`border-slate-600 text-slate-300 hover:text-white hover:bg-slate-700 ${showFilters ? 'bg-slate-700' : ''}`}
|
|
221
|
+
title="Filter memories by type and category"
|
|
222
|
+
>
|
|
223
|
+
Filters {(typeFilter || categoryFilter) && '•'}
|
|
224
|
+
</Button>
|
|
225
|
+
</div>
|
|
226
|
+
<div className="flex items-center gap-2">
|
|
227
|
+
<div className="flex items-center gap-2 text-xs text-slate-400 px-2">
|
|
228
|
+
<span
|
|
229
|
+
className={`w-2 h-2 rounded-full ${isPaused ? 'bg-orange-500 animate-pulse' : (isConnected ? 'bg-green-500' : 'bg-yellow-500')}`}
|
|
230
|
+
title={isPaused ? 'Memory creation paused' : (isConnected ? 'Real-time connected' : 'Polling mode')}
|
|
231
|
+
/>
|
|
232
|
+
{memories.length} memories
|
|
233
|
+
</div>
|
|
234
|
+
</div>
|
|
235
|
+
</header>
|
|
236
|
+
|
|
237
|
+
{/* Filter Bar (collapsible) */}
|
|
238
|
+
{showFilters && (
|
|
239
|
+
<div className="h-12 border-b border-slate-800 flex items-center gap-4 px-4 bg-slate-900/30">
|
|
240
|
+
{/* Type filters */}
|
|
241
|
+
<div className="flex items-center gap-2">
|
|
242
|
+
<span className="text-xs text-slate-400">Type:</span>
|
|
243
|
+
{['short_term', 'long_term', 'episodic'].map((type) => (
|
|
244
|
+
<button
|
|
245
|
+
key={type}
|
|
246
|
+
onClick={() => setTypeFilter(typeFilter === type ? undefined : type)}
|
|
247
|
+
className={`px-2 py-1 text-xs rounded-full transition-colors ${
|
|
248
|
+
typeFilter === type
|
|
249
|
+
? 'bg-blue-600 text-white'
|
|
250
|
+
: 'bg-slate-800 text-slate-300 hover:bg-slate-700'
|
|
251
|
+
}`}
|
|
252
|
+
>
|
|
253
|
+
{type.replace('_', '-')}
|
|
254
|
+
</button>
|
|
255
|
+
))}
|
|
256
|
+
</div>
|
|
257
|
+
|
|
258
|
+
<div className="w-px h-6 bg-slate-700" />
|
|
259
|
+
|
|
260
|
+
{/* Category filters */}
|
|
261
|
+
<div className="flex items-center gap-2">
|
|
262
|
+
<span className="text-xs text-slate-400">Category:</span>
|
|
263
|
+
{['architecture', 'pattern', 'error', 'learning', 'preference', 'context'].map((cat) => (
|
|
264
|
+
<button
|
|
265
|
+
key={cat}
|
|
266
|
+
onClick={() => setCategoryFilter(categoryFilter === cat ? undefined : cat)}
|
|
267
|
+
className={`px-2 py-1 text-xs rounded-full transition-colors ${
|
|
268
|
+
categoryFilter === cat
|
|
269
|
+
? 'bg-purple-600 text-white'
|
|
270
|
+
: 'bg-slate-800 text-slate-300 hover:bg-slate-700'
|
|
271
|
+
}`}
|
|
272
|
+
>
|
|
273
|
+
{cat}
|
|
274
|
+
</button>
|
|
275
|
+
))}
|
|
276
|
+
</div>
|
|
277
|
+
|
|
278
|
+
{/* Clear filters */}
|
|
279
|
+
{(typeFilter || categoryFilter) && (
|
|
280
|
+
<>
|
|
281
|
+
<div className="w-px h-6 bg-slate-700" />
|
|
282
|
+
<button
|
|
283
|
+
onClick={() => {
|
|
284
|
+
setTypeFilter(undefined);
|
|
285
|
+
setCategoryFilter(undefined);
|
|
286
|
+
}}
|
|
287
|
+
className="px-2 py-1 text-xs text-red-400 hover:text-red-300"
|
|
288
|
+
>
|
|
289
|
+
Clear all
|
|
290
|
+
</button>
|
|
291
|
+
</>
|
|
292
|
+
)}
|
|
293
|
+
</div>
|
|
294
|
+
)}
|
|
295
|
+
|
|
296
|
+
{/* Main Content */}
|
|
297
|
+
<div className="flex-1 flex overflow-hidden">
|
|
298
|
+
<NavRail />
|
|
299
|
+
|
|
300
|
+
{/* Active View */}
|
|
301
|
+
<AnimatePresence mode="wait">
|
|
302
|
+
<motion.div
|
|
303
|
+
key={viewMode}
|
|
304
|
+
initial={{ opacity: 0 }}
|
|
305
|
+
animate={{ opacity: 1 }}
|
|
306
|
+
exit={{ opacity: 0 }}
|
|
307
|
+
transition={{ duration: 0.15 }}
|
|
308
|
+
className="flex-1 relative overflow-hidden"
|
|
309
|
+
>
|
|
310
|
+
{viewMode === 'brain' && (
|
|
311
|
+
memoriesLoading ? (
|
|
312
|
+
<div className="w-full h-full flex items-center justify-center">
|
|
313
|
+
<div className="text-slate-400 animate-pulse">Loading memories...</div>
|
|
314
|
+
</div>
|
|
315
|
+
) : (
|
|
316
|
+
<BrainScene
|
|
317
|
+
memories={memories}
|
|
318
|
+
links={links}
|
|
319
|
+
selectedMemory={selectedMemory}
|
|
320
|
+
onSelectMemory={handleSelectMemory}
|
|
321
|
+
/>
|
|
322
|
+
)
|
|
323
|
+
)}
|
|
324
|
+
{viewMode === 'graph' && (
|
|
325
|
+
<KnowledgeGraph
|
|
326
|
+
memories={memories}
|
|
327
|
+
links={links}
|
|
328
|
+
selectedMemory={selectedMemory}
|
|
329
|
+
onSelectMemory={handleSelectMemory}
|
|
330
|
+
/>
|
|
331
|
+
)}
|
|
332
|
+
{viewMode === 'memories' && (
|
|
333
|
+
<MemoriesView
|
|
334
|
+
memories={memories}
|
|
335
|
+
selectedMemory={selectedMemory}
|
|
336
|
+
onSelectMemory={handleSelectMemory}
|
|
337
|
+
/>
|
|
338
|
+
)}
|
|
339
|
+
{viewMode === 'insights' && (
|
|
340
|
+
<InsightsView selectedProject={selectedProject} stats={stats} />
|
|
341
|
+
)}
|
|
342
|
+
{viewMode === 'ontology' && (
|
|
343
|
+
<OntologyGraph />
|
|
344
|
+
)}
|
|
345
|
+
</motion.div>
|
|
346
|
+
</AnimatePresence>
|
|
347
|
+
|
|
348
|
+
{/* Right Detail Panel */}
|
|
349
|
+
{selectedMemory && (
|
|
350
|
+
<div className="w-80 border-l border-slate-800 overflow-y-auto shrink-0">
|
|
351
|
+
<MemoryDetail
|
|
352
|
+
memory={selectedMemory}
|
|
353
|
+
links={links}
|
|
354
|
+
memories={memories}
|
|
355
|
+
onClose={() => setSelectedMemory(null)}
|
|
356
|
+
onReinforce={handleReinforce}
|
|
357
|
+
onSelectMemory={handleSelectMemoryById}
|
|
358
|
+
/>
|
|
359
|
+
</div>
|
|
360
|
+
)}
|
|
361
|
+
</div>
|
|
362
|
+
</div>
|
|
363
|
+
);
|
|
364
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Providers
|
|
5
|
+
* React Query and other providers wrapper
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
9
|
+
import { useState } from 'react';
|
|
10
|
+
|
|
11
|
+
export function Providers({ children }: { children: React.ReactNode }) {
|
|
12
|
+
const [queryClient] = useState(
|
|
13
|
+
() =>
|
|
14
|
+
new QueryClient({
|
|
15
|
+
defaultOptions: {
|
|
16
|
+
queries: {
|
|
17
|
+
staleTime: 5000,
|
|
18
|
+
refetchOnWindowFocus: false,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
})
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Activity Pulse System
|
|
5
|
+
*
|
|
6
|
+
* Creates visual pulses when memory events occur:
|
|
7
|
+
* - memory_created: Green expanding pulse at memory position
|
|
8
|
+
* - memory_accessed: Blue flash at memory position
|
|
9
|
+
* - link_discovered: Cyan line connecting two memories
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { useRef, useMemo } from 'react';
|
|
13
|
+
import { useFrame } from '@react-three/fiber';
|
|
14
|
+
import * as THREE from 'three';
|
|
15
|
+
|
|
16
|
+
interface Pulse {
|
|
17
|
+
id: number;
|
|
18
|
+
type: 'created' | 'accessed' | 'linked';
|
|
19
|
+
position: [number, number, number];
|
|
20
|
+
targetPosition?: [number, number, number]; // For links
|
|
21
|
+
startTime: number;
|
|
22
|
+
duration: number;
|
|
23
|
+
color: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface ActivityPulseSystemProps {
|
|
27
|
+
pulses: Pulse[];
|
|
28
|
+
onPulseComplete: (id: number) => void;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Shared geometry
|
|
32
|
+
const PULSE_GEOMETRY = new THREE.RingGeometry(0.1, 0.15, 32);
|
|
33
|
+
const SPHERE_GEOMETRY = new THREE.SphereGeometry(0.1, 16, 16);
|
|
34
|
+
|
|
35
|
+
function CreatedPulse({
|
|
36
|
+
pulse,
|
|
37
|
+
onComplete,
|
|
38
|
+
}: {
|
|
39
|
+
pulse: Pulse;
|
|
40
|
+
onComplete: () => void;
|
|
41
|
+
}) {
|
|
42
|
+
const meshRef = useRef<THREE.Mesh>(null);
|
|
43
|
+
const startTimeRef = useRef<number | null>(null);
|
|
44
|
+
|
|
45
|
+
useFrame((state) => {
|
|
46
|
+
if (!meshRef.current) return;
|
|
47
|
+
|
|
48
|
+
// Initialize start time on first frame
|
|
49
|
+
if (startTimeRef.current === null) {
|
|
50
|
+
startTimeRef.current = state.clock.elapsedTime;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const elapsed = state.clock.elapsedTime - startTimeRef.current;
|
|
54
|
+
const progress = elapsed / (pulse.duration / 1000); // Convert ms to seconds
|
|
55
|
+
|
|
56
|
+
if (progress >= 1) {
|
|
57
|
+
onComplete();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Expanding ring that fades out
|
|
62
|
+
const scale = 0.5 + progress * 3;
|
|
63
|
+
const opacity = (1 - progress) * 0.8;
|
|
64
|
+
|
|
65
|
+
meshRef.current.scale.setScalar(scale);
|
|
66
|
+
(meshRef.current.material as THREE.MeshBasicMaterial).opacity = opacity;
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<mesh
|
|
71
|
+
ref={meshRef}
|
|
72
|
+
position={pulse.position}
|
|
73
|
+
rotation={[Math.PI / 2, 0, 0]}
|
|
74
|
+
geometry={PULSE_GEOMETRY}
|
|
75
|
+
>
|
|
76
|
+
<meshBasicMaterial
|
|
77
|
+
color={pulse.color}
|
|
78
|
+
transparent
|
|
79
|
+
opacity={0.8}
|
|
80
|
+
side={THREE.DoubleSide}
|
|
81
|
+
depthWrite={false}
|
|
82
|
+
/>
|
|
83
|
+
</mesh>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function AccessedPulse({
|
|
88
|
+
pulse,
|
|
89
|
+
onComplete,
|
|
90
|
+
}: {
|
|
91
|
+
pulse: Pulse;
|
|
92
|
+
onComplete: () => void;
|
|
93
|
+
}) {
|
|
94
|
+
const meshRef = useRef<THREE.Mesh>(null);
|
|
95
|
+
const startTimeRef = useRef<number | null>(null);
|
|
96
|
+
|
|
97
|
+
useFrame((state) => {
|
|
98
|
+
if (!meshRef.current) return;
|
|
99
|
+
|
|
100
|
+
// Initialize start time on first frame
|
|
101
|
+
if (startTimeRef.current === null) {
|
|
102
|
+
startTimeRef.current = state.clock.elapsedTime;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const elapsed = state.clock.elapsedTime - startTimeRef.current;
|
|
106
|
+
const progress = elapsed / (pulse.duration / 1000); // Convert ms to seconds
|
|
107
|
+
|
|
108
|
+
if (progress >= 1) {
|
|
109
|
+
onComplete();
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Flash that expands slightly then fades
|
|
114
|
+
const flash = Math.sin(progress * Math.PI);
|
|
115
|
+
const scale = 0.8 + flash * 0.6;
|
|
116
|
+
const opacity = flash * 0.9;
|
|
117
|
+
|
|
118
|
+
meshRef.current.scale.setScalar(scale);
|
|
119
|
+
(meshRef.current.material as THREE.MeshBasicMaterial).opacity = opacity;
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
return (
|
|
123
|
+
<mesh ref={meshRef} position={pulse.position} geometry={SPHERE_GEOMETRY}>
|
|
124
|
+
<meshBasicMaterial
|
|
125
|
+
color={pulse.color}
|
|
126
|
+
transparent
|
|
127
|
+
opacity={0.9}
|
|
128
|
+
depthWrite={false}
|
|
129
|
+
/>
|
|
130
|
+
</mesh>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function LinkedPulse({
|
|
135
|
+
pulse,
|
|
136
|
+
onComplete,
|
|
137
|
+
}: {
|
|
138
|
+
pulse: Pulse;
|
|
139
|
+
onComplete: () => void;
|
|
140
|
+
}) {
|
|
141
|
+
const groupRef = useRef<THREE.Group>(null);
|
|
142
|
+
const startTimeRef = useRef<number | null>(null);
|
|
143
|
+
const lineRef = useRef<THREE.Line | null>(null);
|
|
144
|
+
|
|
145
|
+
// Create line object once
|
|
146
|
+
const lineObject = useMemo(() => {
|
|
147
|
+
const points = [
|
|
148
|
+
new THREE.Vector3(...pulse.position),
|
|
149
|
+
new THREE.Vector3(...(pulse.targetPosition || pulse.position)),
|
|
150
|
+
];
|
|
151
|
+
const geometry = new THREE.BufferGeometry().setFromPoints(points);
|
|
152
|
+
const material = new THREE.LineBasicMaterial({
|
|
153
|
+
color: pulse.color,
|
|
154
|
+
transparent: true,
|
|
155
|
+
opacity: 0.8,
|
|
156
|
+
});
|
|
157
|
+
return new THREE.Line(geometry, material);
|
|
158
|
+
}, [pulse.position, pulse.targetPosition, pulse.color]);
|
|
159
|
+
|
|
160
|
+
useFrame((state) => {
|
|
161
|
+
if (!lineRef.current) {
|
|
162
|
+
// Add line to group on first render
|
|
163
|
+
if (groupRef.current && !groupRef.current.children.length) {
|
|
164
|
+
groupRef.current.add(lineObject);
|
|
165
|
+
lineRef.current = lineObject;
|
|
166
|
+
}
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Initialize start time on first frame
|
|
171
|
+
if (startTimeRef.current === null) {
|
|
172
|
+
startTimeRef.current = state.clock.elapsedTime;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const elapsed = state.clock.elapsedTime - startTimeRef.current;
|
|
176
|
+
const progress = elapsed / (pulse.duration / 1000); // Convert ms to seconds
|
|
177
|
+
|
|
178
|
+
if (progress >= 1) {
|
|
179
|
+
onComplete();
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Line that appears then fades
|
|
184
|
+
const flash = Math.sin(progress * Math.PI);
|
|
185
|
+
(lineRef.current.material as THREE.LineBasicMaterial).opacity = flash * 0.8;
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
return <group ref={groupRef} />;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function ActivityPulseSystem({ pulses, onPulseComplete }: ActivityPulseSystemProps) {
|
|
192
|
+
return (
|
|
193
|
+
<group name="activity-pulses">
|
|
194
|
+
{pulses.map((pulse) => {
|
|
195
|
+
switch (pulse.type) {
|
|
196
|
+
case 'created':
|
|
197
|
+
return (
|
|
198
|
+
<CreatedPulse
|
|
199
|
+
key={pulse.id}
|
|
200
|
+
pulse={pulse}
|
|
201
|
+
onComplete={() => onPulseComplete(pulse.id)}
|
|
202
|
+
/>
|
|
203
|
+
);
|
|
204
|
+
case 'accessed':
|
|
205
|
+
return (
|
|
206
|
+
<AccessedPulse
|
|
207
|
+
key={pulse.id}
|
|
208
|
+
pulse={pulse}
|
|
209
|
+
onComplete={() => onPulseComplete(pulse.id)}
|
|
210
|
+
/>
|
|
211
|
+
);
|
|
212
|
+
case 'linked':
|
|
213
|
+
return (
|
|
214
|
+
<LinkedPulse
|
|
215
|
+
key={pulse.id}
|
|
216
|
+
pulse={pulse}
|
|
217
|
+
onComplete={() => onPulseComplete(pulse.id)}
|
|
218
|
+
/>
|
|
219
|
+
);
|
|
220
|
+
default:
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
})}
|
|
224
|
+
</group>
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Type export for use in parent components
|
|
229
|
+
export type { Pulse };
|