claude-cortex 1.0.0 → 1.1.1
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/dashboard/README.md +36 -0
- package/dashboard/components.json +22 -0
- package/dashboard/eslint.config.mjs +18 -0
- package/dashboard/next.config.ts +7 -0
- package/dashboard/package-lock.json +7784 -0
- package/dashboard/package.json +42 -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/src/app/favicon.ico +0 -0
- package/dashboard/src/app/globals.css +125 -0
- package/dashboard/src/app/layout.tsx +35 -0
- package/dashboard/src/app/page.tsx +338 -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 +118 -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 +306 -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 +100 -0
- package/dashboard/src/components/dashboard/StatsPanel.tsx +164 -0
- package/dashboard/src/components/debug/ActivityLog.tsx +238 -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 +313 -0
- package/dashboard/src/components/memory/MemoryDetail.tsx +325 -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 +276 -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 +216 -0
- package/dashboard/src/types/memory.ts +73 -0
- package/dashboard/tsconfig.json +34 -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/visualization-server.d.ts.map +1 -1
- package/dist/api/visualization-server.js +109 -2
- package/dist/api/visualization-server.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +80 -4
- package/dist/index.js.map +1 -1
- package/dist/memory/store.d.ts +6 -0
- package/dist/memory/store.d.ts.map +1 -1
- package/dist/memory/store.js +14 -0
- package/dist/memory/store.js.map +1 -1
- package/package.json +7 -3
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dashboard",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "next dev -p 3030",
|
|
7
|
+
"build": "next build",
|
|
8
|
+
"start": "next start",
|
|
9
|
+
"lint": "eslint"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@radix-ui/react-slot": "^1.2.4",
|
|
13
|
+
"@react-three/drei": "^10.7.7",
|
|
14
|
+
"@react-three/fiber": "^9.5.0",
|
|
15
|
+
"@react-three/postprocessing": "^3.0.4",
|
|
16
|
+
"@tanstack/react-query": "^5.90.19",
|
|
17
|
+
"@types/three": "^0.182.0",
|
|
18
|
+
"class-variance-authority": "^0.7.1",
|
|
19
|
+
"clsx": "^2.1.1",
|
|
20
|
+
"framer-motion": "^12.29.0",
|
|
21
|
+
"lucide-react": "^0.562.0",
|
|
22
|
+
"next": "16.1.4",
|
|
23
|
+
"react": "19.2.3",
|
|
24
|
+
"react-dom": "19.2.3",
|
|
25
|
+
"react-resizable-panels": "^4.4.1",
|
|
26
|
+
"recharts": "^3.7.0",
|
|
27
|
+
"tailwind-merge": "^3.4.0",
|
|
28
|
+
"three": "^0.182.0",
|
|
29
|
+
"zustand": "^5.0.10"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@tailwindcss/postcss": "^4",
|
|
33
|
+
"@types/node": "^20",
|
|
34
|
+
"@types/react": "^19",
|
|
35
|
+
"@types/react-dom": "^19",
|
|
36
|
+
"eslint": "^9",
|
|
37
|
+
"eslint-config-next": "16.1.4",
|
|
38
|
+
"tailwindcss": "^4",
|
|
39
|
+
"tw-animate-css": "^1.4.0",
|
|
40
|
+
"typescript": "^5"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
|
|
Binary file
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
@import "tw-animate-css";
|
|
3
|
+
|
|
4
|
+
@custom-variant dark (&:is(.dark *));
|
|
5
|
+
|
|
6
|
+
@theme inline {
|
|
7
|
+
--color-background: var(--background);
|
|
8
|
+
--color-foreground: var(--foreground);
|
|
9
|
+
--font-sans: var(--font-geist-sans);
|
|
10
|
+
--font-mono: var(--font-geist-mono);
|
|
11
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
12
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
13
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
14
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
15
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
16
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
17
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
18
|
+
--color-sidebar: var(--sidebar);
|
|
19
|
+
--color-chart-5: var(--chart-5);
|
|
20
|
+
--color-chart-4: var(--chart-4);
|
|
21
|
+
--color-chart-3: var(--chart-3);
|
|
22
|
+
--color-chart-2: var(--chart-2);
|
|
23
|
+
--color-chart-1: var(--chart-1);
|
|
24
|
+
--color-ring: var(--ring);
|
|
25
|
+
--color-input: var(--input);
|
|
26
|
+
--color-border: var(--border);
|
|
27
|
+
--color-destructive: var(--destructive);
|
|
28
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
29
|
+
--color-accent: var(--accent);
|
|
30
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
31
|
+
--color-muted: var(--muted);
|
|
32
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
33
|
+
--color-secondary: var(--secondary);
|
|
34
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
35
|
+
--color-primary: var(--primary);
|
|
36
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
37
|
+
--color-popover: var(--popover);
|
|
38
|
+
--color-card-foreground: var(--card-foreground);
|
|
39
|
+
--color-card: var(--card);
|
|
40
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
41
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
42
|
+
--radius-lg: var(--radius);
|
|
43
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
44
|
+
--radius-2xl: calc(var(--radius) + 8px);
|
|
45
|
+
--radius-3xl: calc(var(--radius) + 12px);
|
|
46
|
+
--radius-4xl: calc(var(--radius) + 16px);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
:root {
|
|
50
|
+
--radius: 0.625rem;
|
|
51
|
+
--background: oklch(1 0 0);
|
|
52
|
+
--foreground: oklch(0.145 0 0);
|
|
53
|
+
--card: oklch(1 0 0);
|
|
54
|
+
--card-foreground: oklch(0.145 0 0);
|
|
55
|
+
--popover: oklch(1 0 0);
|
|
56
|
+
--popover-foreground: oklch(0.145 0 0);
|
|
57
|
+
--primary: oklch(0.205 0 0);
|
|
58
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
59
|
+
--secondary: oklch(0.97 0 0);
|
|
60
|
+
--secondary-foreground: oklch(0.205 0 0);
|
|
61
|
+
--muted: oklch(0.97 0 0);
|
|
62
|
+
--muted-foreground: oklch(0.556 0 0);
|
|
63
|
+
--accent: oklch(0.97 0 0);
|
|
64
|
+
--accent-foreground: oklch(0.205 0 0);
|
|
65
|
+
--destructive: oklch(0.577 0.245 27.325);
|
|
66
|
+
--border: oklch(0.922 0 0);
|
|
67
|
+
--input: oklch(0.922 0 0);
|
|
68
|
+
--ring: oklch(0.708 0 0);
|
|
69
|
+
--chart-1: oklch(0.646 0.222 41.116);
|
|
70
|
+
--chart-2: oklch(0.6 0.118 184.704);
|
|
71
|
+
--chart-3: oklch(0.398 0.07 227.392);
|
|
72
|
+
--chart-4: oklch(0.828 0.189 84.429);
|
|
73
|
+
--chart-5: oklch(0.769 0.188 70.08);
|
|
74
|
+
--sidebar: oklch(0.985 0 0);
|
|
75
|
+
--sidebar-foreground: oklch(0.145 0 0);
|
|
76
|
+
--sidebar-primary: oklch(0.205 0 0);
|
|
77
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
78
|
+
--sidebar-accent: oklch(0.97 0 0);
|
|
79
|
+
--sidebar-accent-foreground: oklch(0.205 0 0);
|
|
80
|
+
--sidebar-border: oklch(0.922 0 0);
|
|
81
|
+
--sidebar-ring: oklch(0.708 0 0);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.dark {
|
|
85
|
+
--background: oklch(0.145 0 0);
|
|
86
|
+
--foreground: oklch(0.985 0 0);
|
|
87
|
+
--card: oklch(0.205 0 0);
|
|
88
|
+
--card-foreground: oklch(0.985 0 0);
|
|
89
|
+
--popover: oklch(0.205 0 0);
|
|
90
|
+
--popover-foreground: oklch(0.985 0 0);
|
|
91
|
+
--primary: oklch(0.922 0 0);
|
|
92
|
+
--primary-foreground: oklch(0.205 0 0);
|
|
93
|
+
--secondary: oklch(0.269 0 0);
|
|
94
|
+
--secondary-foreground: oklch(0.985 0 0);
|
|
95
|
+
--muted: oklch(0.269 0 0);
|
|
96
|
+
--muted-foreground: oklch(0.708 0 0);
|
|
97
|
+
--accent: oklch(0.269 0 0);
|
|
98
|
+
--accent-foreground: oklch(0.985 0 0);
|
|
99
|
+
--destructive: oklch(0.704 0.191 22.216);
|
|
100
|
+
--border: oklch(1 0 0 / 10%);
|
|
101
|
+
--input: oklch(1 0 0 / 15%);
|
|
102
|
+
--ring: oklch(0.556 0 0);
|
|
103
|
+
--chart-1: oklch(0.488 0.243 264.376);
|
|
104
|
+
--chart-2: oklch(0.696 0.17 162.48);
|
|
105
|
+
--chart-3: oklch(0.769 0.188 70.08);
|
|
106
|
+
--chart-4: oklch(0.627 0.265 303.9);
|
|
107
|
+
--chart-5: oklch(0.645 0.246 16.439);
|
|
108
|
+
--sidebar: oklch(0.205 0 0);
|
|
109
|
+
--sidebar-foreground: oklch(0.985 0 0);
|
|
110
|
+
--sidebar-primary: oklch(0.488 0.243 264.376);
|
|
111
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
112
|
+
--sidebar-accent: oklch(0.269 0 0);
|
|
113
|
+
--sidebar-accent-foreground: oklch(0.985 0 0);
|
|
114
|
+
--sidebar-border: oklch(1 0 0 / 10%);
|
|
115
|
+
--sidebar-ring: oklch(0.556 0 0);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
@layer base {
|
|
119
|
+
* {
|
|
120
|
+
@apply border-border outline-ring/50;
|
|
121
|
+
}
|
|
122
|
+
body {
|
|
123
|
+
@apply bg-background text-foreground;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Metadata } from "next";
|
|
2
|
+
import { Geist, Geist_Mono } from "next/font/google";
|
|
3
|
+
import "./globals.css";
|
|
4
|
+
import { Providers } from "@/components/Providers";
|
|
5
|
+
|
|
6
|
+
const geistSans = Geist({
|
|
7
|
+
variable: "--font-geist-sans",
|
|
8
|
+
subsets: ["latin"],
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const geistMono = Geist_Mono({
|
|
12
|
+
variable: "--font-geist-mono",
|
|
13
|
+
subsets: ["latin"],
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export const metadata: Metadata = {
|
|
17
|
+
title: "Claude Cortex",
|
|
18
|
+
description: "AI Brain Visualization for Claude Cortex Memory System",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default function RootLayout({
|
|
22
|
+
children,
|
|
23
|
+
}: Readonly<{
|
|
24
|
+
children: React.ReactNode;
|
|
25
|
+
}>) {
|
|
26
|
+
return (
|
|
27
|
+
<html lang="en" className="dark">
|
|
28
|
+
<body
|
|
29
|
+
className={`${geistSans.variable} ${geistMono.variable} antialiased bg-slate-950`}
|
|
30
|
+
>
|
|
31
|
+
<Providers>{children}</Providers>
|
|
32
|
+
</body>
|
|
33
|
+
</html>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Main Dashboard Page
|
|
5
|
+
* Brain-like visualization of the Claude Cortex memory system
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { useState, useRef, useEffect } from 'react';
|
|
9
|
+
import dynamic from 'next/dynamic';
|
|
10
|
+
import { useMemoriesWithRealtime, useStats, useAccessMemory, useConsolidate, useProjects, useMemoryLinks, useControlStatus, usePauseMemory, useResumeMemory } from '@/hooks/useMemories';
|
|
11
|
+
import { useDashboardStore } from '@/lib/store';
|
|
12
|
+
import { useDebouncedValue } from '@/hooks/useDebouncedValue';
|
|
13
|
+
import { useSuggestions } from '@/hooks/useSuggestions';
|
|
14
|
+
import { StatsPanel } from '@/components/dashboard/StatsPanel';
|
|
15
|
+
import { MemoryDetail } from '@/components/memory/MemoryDetail';
|
|
16
|
+
import { ControlPanel } from '@/components/controls/ControlPanel';
|
|
17
|
+
import { DebugPanel } from '@/components/debug/DebugPanel';
|
|
18
|
+
import { Button } from '@/components/ui/button';
|
|
19
|
+
import { Input } from '@/components/ui/input';
|
|
20
|
+
import { Memory } from '@/types/memory';
|
|
21
|
+
|
|
22
|
+
// Dynamic import for 3D scene (avoid SSR issues)
|
|
23
|
+
const BrainScene = dynamic(
|
|
24
|
+
() => import('@/components/brain/BrainScene').then((mod) => mod.BrainScene),
|
|
25
|
+
{
|
|
26
|
+
ssr: false,
|
|
27
|
+
loading: () => (
|
|
28
|
+
<div className="w-full h-full flex items-center justify-center bg-slate-950">
|
|
29
|
+
<div className="text-slate-400 animate-pulse">Loading 3D Brain...</div>
|
|
30
|
+
</div>
|
|
31
|
+
),
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
export default function DashboardPage() {
|
|
36
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
37
|
+
const [showSuggestions, setShowSuggestions] = useState(false);
|
|
38
|
+
const [selectedProject, setSelectedProject] = useState<string | undefined>(undefined);
|
|
39
|
+
const [showFilters, setShowFilters] = useState(false);
|
|
40
|
+
const [typeFilter, setTypeFilter] = useState<string | undefined>(undefined);
|
|
41
|
+
const [categoryFilter, setCategoryFilter] = useState<string | undefined>(undefined);
|
|
42
|
+
const searchInputRef = useRef<HTMLInputElement>(null);
|
|
43
|
+
const suggestionsRef = useRef<HTMLDivElement>(null);
|
|
44
|
+
|
|
45
|
+
// Debounce search to avoid API calls on every keystroke
|
|
46
|
+
const debouncedSearch = useDebouncedValue(searchQuery, 300);
|
|
47
|
+
|
|
48
|
+
// Zustand store
|
|
49
|
+
const { selectedMemory, setSelectedMemory } = useDashboardStore();
|
|
50
|
+
|
|
51
|
+
// Search suggestions
|
|
52
|
+
const { data: suggestions = [] } = useSuggestions(searchQuery);
|
|
53
|
+
|
|
54
|
+
// Fetch projects for dropdown
|
|
55
|
+
const { data: projectsData } = useProjects();
|
|
56
|
+
|
|
57
|
+
// Close suggestions when clicking outside
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
const handleClickOutside = (event: MouseEvent) => {
|
|
60
|
+
if (
|
|
61
|
+
suggestionsRef.current &&
|
|
62
|
+
!suggestionsRef.current.contains(event.target as Node) &&
|
|
63
|
+
searchInputRef.current &&
|
|
64
|
+
!searchInputRef.current.contains(event.target as Node)
|
|
65
|
+
) {
|
|
66
|
+
setShowSuggestions(false);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
71
|
+
return () => document.removeEventListener('mousedown', handleClickOutside);
|
|
72
|
+
}, []);
|
|
73
|
+
|
|
74
|
+
const handleSelectSuggestion = (text: string) => {
|
|
75
|
+
setSearchQuery(text);
|
|
76
|
+
setShowSuggestions(false);
|
|
77
|
+
searchInputRef.current?.focus();
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// Data fetching with real-time WebSocket updates
|
|
81
|
+
const {
|
|
82
|
+
data: memories = [],
|
|
83
|
+
isLoading: memoriesLoading,
|
|
84
|
+
isConnected,
|
|
85
|
+
} = useMemoriesWithRealtime({
|
|
86
|
+
limit: 200,
|
|
87
|
+
query: debouncedSearch || undefined,
|
|
88
|
+
mode: debouncedSearch ? 'search' : 'recent',
|
|
89
|
+
project: selectedProject,
|
|
90
|
+
type: typeFilter,
|
|
91
|
+
category: categoryFilter,
|
|
92
|
+
});
|
|
93
|
+
const { data: stats, isLoading: statsLoading } = useStats(selectedProject);
|
|
94
|
+
const { data: links = [] } = useMemoryLinks(selectedProject);
|
|
95
|
+
|
|
96
|
+
// Mutations
|
|
97
|
+
const accessMutation = useAccessMemory();
|
|
98
|
+
const consolidateMutation = useConsolidate();
|
|
99
|
+
|
|
100
|
+
// Control status
|
|
101
|
+
const { data: controlStatus } = useControlStatus();
|
|
102
|
+
const pauseMutation = usePauseMemory();
|
|
103
|
+
const resumeMutation = useResumeMemory();
|
|
104
|
+
const isPaused = controlStatus?.paused ?? false;
|
|
105
|
+
|
|
106
|
+
const handleSelectMemory = (memory: Memory | null) => {
|
|
107
|
+
setSelectedMemory(memory);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const handleSelectMemoryById = (id: number) => {
|
|
111
|
+
const memory = memories.find(m => m.id === id);
|
|
112
|
+
if (memory) {
|
|
113
|
+
setSelectedMemory(memory);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const handleReinforce = (id: number) => {
|
|
118
|
+
accessMutation.mutate(id);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const handleConsolidate = () => {
|
|
122
|
+
consolidateMutation.mutate();
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const [showDebugPanel, setShowDebugPanel] = useState(true);
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<div className="h-screen w-screen bg-slate-950 text-white overflow-hidden flex flex-col">
|
|
129
|
+
{/* Top Bar */}
|
|
130
|
+
<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">
|
|
131
|
+
<div className="flex items-center gap-4">
|
|
132
|
+
<h1 className="text-xl font-bold bg-gradient-to-r from-blue-400 via-purple-500 to-pink-500 bg-clip-text text-transparent">
|
|
133
|
+
🧠 Claude Cortex
|
|
134
|
+
</h1>
|
|
135
|
+
|
|
136
|
+
{/* Project Selector */}
|
|
137
|
+
<select
|
|
138
|
+
value={selectedProject || ''}
|
|
139
|
+
onChange={(e) => setSelectedProject(e.target.value || undefined)}
|
|
140
|
+
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"
|
|
141
|
+
>
|
|
142
|
+
{projectsData?.projects.map((p) => (
|
|
143
|
+
<option key={p.project || 'all'} value={p.project || ''}>
|
|
144
|
+
{p.label} ({p.memory_count})
|
|
145
|
+
</option>
|
|
146
|
+
))}
|
|
147
|
+
</select>
|
|
148
|
+
|
|
149
|
+
{/* Search Input */}
|
|
150
|
+
<div className="relative">
|
|
151
|
+
<Input
|
|
152
|
+
ref={searchInputRef}
|
|
153
|
+
type="text"
|
|
154
|
+
placeholder="Search memories..."
|
|
155
|
+
value={searchQuery}
|
|
156
|
+
onChange={(e) => {
|
|
157
|
+
setSearchQuery(e.target.value);
|
|
158
|
+
setShowSuggestions(true);
|
|
159
|
+
}}
|
|
160
|
+
onFocus={() => setShowSuggestions(true)}
|
|
161
|
+
onKeyDown={(e) => {
|
|
162
|
+
if (e.key === 'Escape') {
|
|
163
|
+
setShowSuggestions(false);
|
|
164
|
+
}
|
|
165
|
+
}}
|
|
166
|
+
className="w-64 bg-slate-800 border-slate-700 text-white placeholder:text-slate-400 focus:ring-blue-500"
|
|
167
|
+
/>
|
|
168
|
+
{/* Suggestions dropdown */}
|
|
169
|
+
{showSuggestions && suggestions.length > 0 && (
|
|
170
|
+
<div
|
|
171
|
+
ref={suggestionsRef}
|
|
172
|
+
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"
|
|
173
|
+
>
|
|
174
|
+
{suggestions.map((suggestion, index) => (
|
|
175
|
+
<button
|
|
176
|
+
key={`${suggestion.type}-${suggestion.text}-${index}`}
|
|
177
|
+
onClick={() => handleSelectSuggestion(suggestion.text)}
|
|
178
|
+
className="w-full px-3 py-2 text-left hover:bg-slate-700 transition-colors flex items-center gap-2"
|
|
179
|
+
>
|
|
180
|
+
<span className="text-white text-sm truncate flex-1">
|
|
181
|
+
{suggestion.text}
|
|
182
|
+
</span>
|
|
183
|
+
<span className="text-xs px-1.5 py-0.5 rounded bg-slate-600 text-slate-300">
|
|
184
|
+
{suggestion.type}
|
|
185
|
+
</span>
|
|
186
|
+
</button>
|
|
187
|
+
))}
|
|
188
|
+
</div>
|
|
189
|
+
)}
|
|
190
|
+
</div>
|
|
191
|
+
|
|
192
|
+
{/* Filter Toggle */}
|
|
193
|
+
<Button
|
|
194
|
+
variant="outline"
|
|
195
|
+
size="sm"
|
|
196
|
+
onClick={() => setShowFilters(!showFilters)}
|
|
197
|
+
className={`border-slate-600 text-slate-300 hover:text-white hover:bg-slate-700 ${showFilters ? 'bg-slate-700' : ''}`}
|
|
198
|
+
>
|
|
199
|
+
Filters {(typeFilter || categoryFilter) && '•'}
|
|
200
|
+
</Button>
|
|
201
|
+
</div>
|
|
202
|
+
<div className="flex items-center gap-2">
|
|
203
|
+
{/* Quick Pause/Resume Toggle */}
|
|
204
|
+
<Button
|
|
205
|
+
variant="outline"
|
|
206
|
+
size="sm"
|
|
207
|
+
onClick={() => isPaused ? resumeMutation.mutate() : pauseMutation.mutate()}
|
|
208
|
+
disabled={pauseMutation.isPending || resumeMutation.isPending}
|
|
209
|
+
className={`${
|
|
210
|
+
isPaused
|
|
211
|
+
? 'border-orange-600 bg-orange-600/20 text-orange-300 hover:bg-orange-600/30'
|
|
212
|
+
: 'border-slate-600 text-slate-300 hover:text-white hover:bg-slate-700'
|
|
213
|
+
}`}
|
|
214
|
+
>
|
|
215
|
+
{isPaused ? '▶ Resume' : '⏸ Pause'}
|
|
216
|
+
</Button>
|
|
217
|
+
<Button
|
|
218
|
+
variant="outline"
|
|
219
|
+
size="sm"
|
|
220
|
+
onClick={handleConsolidate}
|
|
221
|
+
disabled={consolidateMutation.isPending}
|
|
222
|
+
className="border-slate-600 text-slate-300 hover:text-white hover:bg-slate-700"
|
|
223
|
+
>
|
|
224
|
+
{consolidateMutation.isPending ? '...' : '🔄'}
|
|
225
|
+
</Button>
|
|
226
|
+
<div className="flex items-center gap-2 text-xs text-slate-400 px-2">
|
|
227
|
+
<span
|
|
228
|
+
className={`w-2 h-2 rounded-full ${isPaused ? 'bg-orange-500 animate-pulse' : (isConnected ? 'bg-green-500' : 'bg-yellow-500')}`}
|
|
229
|
+
title={isPaused ? 'Memory creation paused' : (isConnected ? 'Real-time connected' : 'Polling mode')}
|
|
230
|
+
/>
|
|
231
|
+
{memories.length} memories
|
|
232
|
+
</div>
|
|
233
|
+
</div>
|
|
234
|
+
</header>
|
|
235
|
+
|
|
236
|
+
{/* Filter Bar (collapsible) */}
|
|
237
|
+
{showFilters && (
|
|
238
|
+
<div className="h-12 border-b border-slate-800 flex items-center gap-4 px-4 bg-slate-900/30">
|
|
239
|
+
{/* Type filters */}
|
|
240
|
+
<div className="flex items-center gap-2">
|
|
241
|
+
<span className="text-xs text-slate-400">Type:</span>
|
|
242
|
+
{['short_term', 'long_term', 'episodic'].map((type) => (
|
|
243
|
+
<button
|
|
244
|
+
key={type}
|
|
245
|
+
onClick={() => setTypeFilter(typeFilter === type ? undefined : type)}
|
|
246
|
+
className={`px-2 py-1 text-xs rounded-full transition-colors ${
|
|
247
|
+
typeFilter === type
|
|
248
|
+
? 'bg-blue-600 text-white'
|
|
249
|
+
: 'bg-slate-800 text-slate-300 hover:bg-slate-700'
|
|
250
|
+
}`}
|
|
251
|
+
>
|
|
252
|
+
{type.replace('_', '-')}
|
|
253
|
+
</button>
|
|
254
|
+
))}
|
|
255
|
+
</div>
|
|
256
|
+
|
|
257
|
+
<div className="w-px h-6 bg-slate-700" />
|
|
258
|
+
|
|
259
|
+
{/* Category filters */}
|
|
260
|
+
<div className="flex items-center gap-2">
|
|
261
|
+
<span className="text-xs text-slate-400">Category:</span>
|
|
262
|
+
{['architecture', 'pattern', 'error', 'learning', 'preference', 'context'].map((cat) => (
|
|
263
|
+
<button
|
|
264
|
+
key={cat}
|
|
265
|
+
onClick={() => setCategoryFilter(categoryFilter === cat ? undefined : cat)}
|
|
266
|
+
className={`px-2 py-1 text-xs rounded-full transition-colors ${
|
|
267
|
+
categoryFilter === cat
|
|
268
|
+
? 'bg-purple-600 text-white'
|
|
269
|
+
: 'bg-slate-800 text-slate-300 hover:bg-slate-700'
|
|
270
|
+
}`}
|
|
271
|
+
>
|
|
272
|
+
{cat}
|
|
273
|
+
</button>
|
|
274
|
+
))}
|
|
275
|
+
</div>
|
|
276
|
+
|
|
277
|
+
{/* Clear filters */}
|
|
278
|
+
{(typeFilter || categoryFilter) && (
|
|
279
|
+
<>
|
|
280
|
+
<div className="w-px h-6 bg-slate-700" />
|
|
281
|
+
<button
|
|
282
|
+
onClick={() => {
|
|
283
|
+
setTypeFilter(undefined);
|
|
284
|
+
setCategoryFilter(undefined);
|
|
285
|
+
}}
|
|
286
|
+
className="px-2 py-1 text-xs text-red-400 hover:text-red-300"
|
|
287
|
+
>
|
|
288
|
+
Clear all
|
|
289
|
+
</button>
|
|
290
|
+
</>
|
|
291
|
+
)}
|
|
292
|
+
</div>
|
|
293
|
+
)}
|
|
294
|
+
|
|
295
|
+
{/* Main Content */}
|
|
296
|
+
<div className="flex-1 flex overflow-hidden">
|
|
297
|
+
{/* Left Sidebar - Stats & Controls */}
|
|
298
|
+
<div className="w-64 border-r border-slate-800 overflow-y-auto p-4 bg-slate-900/30 shrink-0 space-y-4">
|
|
299
|
+
<StatsPanel stats={stats} isLoading={statsLoading} />
|
|
300
|
+
<ControlPanel />
|
|
301
|
+
</div>
|
|
302
|
+
|
|
303
|
+
{/* Center - Brain Visualization */}
|
|
304
|
+
<div className="flex-1 relative">
|
|
305
|
+
{memoriesLoading ? (
|
|
306
|
+
<div className="w-full h-full flex items-center justify-center">
|
|
307
|
+
<div className="text-slate-400 animate-pulse">Loading memories...</div>
|
|
308
|
+
</div>
|
|
309
|
+
) : (
|
|
310
|
+
<BrainScene
|
|
311
|
+
memories={memories}
|
|
312
|
+
links={links}
|
|
313
|
+
selectedMemory={selectedMemory}
|
|
314
|
+
onSelectMemory={handleSelectMemory}
|
|
315
|
+
/>
|
|
316
|
+
)}
|
|
317
|
+
</div>
|
|
318
|
+
|
|
319
|
+
{/* Right Sidebar - Details (conditional) */}
|
|
320
|
+
{selectedMemory && (
|
|
321
|
+
<div className="w-80 border-l border-slate-800 overflow-y-auto shrink-0">
|
|
322
|
+
<MemoryDetail
|
|
323
|
+
memory={selectedMemory}
|
|
324
|
+
links={links}
|
|
325
|
+
memories={memories}
|
|
326
|
+
onClose={() => setSelectedMemory(null)}
|
|
327
|
+
onReinforce={handleReinforce}
|
|
328
|
+
onSelectMemory={handleSelectMemoryById}
|
|
329
|
+
/>
|
|
330
|
+
</div>
|
|
331
|
+
)}
|
|
332
|
+
</div>
|
|
333
|
+
|
|
334
|
+
{/* Debug Panel (bottom) */}
|
|
335
|
+
{showDebugPanel && <DebugPanel />}
|
|
336
|
+
</div>
|
|
337
|
+
);
|
|
338
|
+
}
|
|
@@ -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
|
+
}
|