@cybermem/dashboard 0.5.10 → 0.5.14

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.
@@ -1,4 +1,4 @@
1
- "use client"
1
+ "use client";
2
2
 
3
3
  import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
4
4
  import { useDashboard } from "@/lib/data/dashboard-context";
@@ -10,7 +10,7 @@ import { useEffect, useRef, useState } from "react";
10
10
  const MetricsChart = dynamic(() => import("./metrics-chart"), { ssr: false });
11
11
 
12
12
  interface ChartCardProps {
13
- service: string
13
+ service: string;
14
14
  }
15
15
 
16
16
  // Fallback color generator
@@ -19,10 +19,10 @@ function stringToColor(str: string): string {
19
19
  for (let i = 0; i < str.length; i++) {
20
20
  hash = str.charCodeAt(i) + ((hash << 5) - hash);
21
21
  }
22
- let color = '#';
22
+ let color = "#";
23
23
  for (let i = 0; i < 3; i++) {
24
- const value = (hash >> (i * 8)) & 0xFF;
25
- color += ('00' + value.toString(16)).substr(-2);
24
+ const value = (hash >> (i * 8)) & 0xff;
25
+ color += ("00" + value.toString(16)).substr(-2);
26
26
  }
27
27
  return color;
28
28
  }
@@ -33,108 +33,133 @@ const periods = [
33
33
  { value: "7d", label: "7 Days" },
34
34
  { value: "30d", label: "30 Days" },
35
35
  { value: "90d", label: "90 Days" },
36
- ]
36
+ ];
37
37
 
38
38
  export default function ChartCard({ service }: ChartCardProps) {
39
- const { strategy, refreshSignal, clientConfigs } = useDashboard()
40
- const [period, setPeriod] = useState("24h")
41
- const [hovered, setHovered] = useState<string | null>(null)
42
- const [data, setData] = useState<any[]>([])
43
- const [clientNames, setClientNames] = useState<string[]>([])
44
- const [clientMetadata, setClientMetadata] = useState<Record<string, any>>({})
45
- const [loading, setLoading] = useState(true)
46
-
47
- const isInitialLoad = useRef(true)
39
+ const { strategy, refreshSignal, clientConfigs } = useDashboard();
40
+ const [period, setPeriod] = useState("24h");
41
+ const [hovered, setHovered] = useState<string | null>(null);
42
+ const [data, setData] = useState<any[]>([]);
43
+ const [clientNames, setClientNames] = useState<string[]>([]);
44
+ const [clientMetadata, setClientMetadata] = useState<Record<string, any>>({});
45
+ const [loading, setLoading] = useState(true);
46
+
47
+ const isInitialLoad = useRef(true);
48
48
  useEffect(() => {
49
49
  async function fetchData() {
50
50
  // Only show loading state on initial load, not background refresh
51
- if (isInitialLoad.current) setLoading(true)
51
+ if (isInitialLoad.current) setLoading(true);
52
52
 
53
53
  try {
54
- const timeSeriesData = await strategy.getChartData(period)
54
+ const timeSeriesData = await strategy.getChartData(period);
55
55
 
56
56
  // Update client metadata if provided in response
57
57
  if (timeSeriesData.metadata) {
58
- setClientMetadata(prev => ({ ...prev, ...timeSeriesData.metadata }))
58
+ setClientMetadata((prev) => ({
59
+ ...prev,
60
+ ...timeSeriesData.metadata,
61
+ }));
59
62
  }
60
63
 
61
64
  // Helper to format time based on period
62
65
  const formatSeries = (series: any[]) => {
63
- if (!series) return []
64
- return series.map(point => {
65
- const date = new Date((point.time as number) * 1000)
66
- let timeStr = ""
66
+ if (!series) return [];
67
+ return series.map((point) => {
68
+ const date = new Date((point.time as number) * 1000);
69
+ let timeStr = "";
67
70
  // Show date if period is longer than 24h
68
71
  if (["7d", "30d", "90d", "all"].includes(period)) {
69
- timeStr = date.toLocaleDateString([], { month: '2-digit', day: '2-digit' }) + " " + date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
72
+ timeStr =
73
+ date.toLocaleDateString([], {
74
+ month: "2-digit",
75
+ day: "2-digit",
76
+ }) +
77
+ " " +
78
+ date.toLocaleTimeString([], {
79
+ hour: "2-digit",
80
+ minute: "2-digit",
81
+ });
70
82
  } else {
71
- timeStr = date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
83
+ timeStr = date.toLocaleTimeString([], {
84
+ hour: "2-digit",
85
+ minute: "2-digit",
86
+ });
72
87
  }
73
88
  return {
74
- ...point,
75
- time: timeStr
76
- }
77
- })
78
- }
89
+ ...point,
90
+ time: timeStr,
91
+ };
92
+ });
93
+ };
79
94
 
80
95
  // Extract client names from series and sort by total value (Ascending)
81
96
  const getClients = (series: any[]) => {
82
- if (!series || series.length === 0) return []
83
- const keys = new Set<string>()
84
- const totals: Record<string, number> = {}
85
-
86
- series.forEach(point => {
87
- Object.keys(point).forEach(k => {
88
- if (k !== 'time') {
89
- keys.add(k)
90
- totals[k] = (totals[k] || 0) + (point[k] as number)
91
- }
92
- })
93
- })
94
-
95
- return Array.from(keys).sort((a, b) => (totals[a] || 0) - (totals[b] || 0))
96
- }
97
+ if (!series || series.length === 0) return [];
98
+ const keys = new Set<string>();
99
+ const totals: Record<string, number> = {};
100
+
101
+ series.forEach((point) => {
102
+ Object.keys(point).forEach((k) => {
103
+ if (k !== "time") {
104
+ keys.add(k);
105
+ totals[k] = (totals[k] || 0) + (point[k] as number);
106
+ }
107
+ });
108
+ });
109
+
110
+ return Array.from(keys).sort(
111
+ (a, b) => (totals[a] || 0) - (totals[b] || 0),
112
+ );
113
+ };
97
114
 
98
115
  // Get data based on service type
99
- let seriesData: any[] = []
100
- let clients: string[] = []
116
+ let seriesData: any[] = [];
117
+ let clients: string[] = [];
101
118
 
102
119
  if (service.includes("Creates")) {
103
- seriesData = formatSeries(timeSeriesData.creates)
104
- clients = getClients(timeSeriesData.creates)
120
+ seriesData = formatSeries(timeSeriesData.creates);
121
+ clients = getClients(timeSeriesData.creates);
105
122
  } else if (service.includes("Reads")) {
106
- seriesData = formatSeries(timeSeriesData.reads)
107
- clients = getClients(timeSeriesData.reads)
123
+ seriesData = formatSeries(timeSeriesData.reads);
124
+ clients = getClients(timeSeriesData.reads);
108
125
  } else if (service.includes("Updates")) {
109
- seriesData = formatSeries(timeSeriesData.updates)
110
- clients = getClients(timeSeriesData.updates)
126
+ seriesData = formatSeries(timeSeriesData.updates);
127
+ clients = getClients(timeSeriesData.updates);
111
128
  } else if (service.includes("Deletes")) {
112
- seriesData = formatSeries(timeSeriesData.deletes)
113
- clients = getClients(timeSeriesData.deletes)
129
+ seriesData = formatSeries(timeSeriesData.deletes);
130
+ clients = getClients(timeSeriesData.deletes);
114
131
  }
115
132
 
116
- setData(seriesData)
117
- setClientNames(clients)
118
-
133
+ setData(seriesData);
134
+ setClientNames(clients);
119
135
  } catch (e) {
120
- console.error("Failed to fetch chart data:", e)
136
+ console.error("Failed to fetch chart data:", e);
121
137
  } finally {
122
- setLoading(false)
123
- isInitialLoad.current = false
138
+ setLoading(false);
139
+ isInitialLoad.current = false;
124
140
  }
125
141
  }
126
- fetchData()
127
- }, [period, service, strategy, refreshSignal])
142
+ fetchData();
143
+ }, [period, service, strategy, refreshSignal]);
128
144
 
129
- const isMultiSeries = clientNames.length > 0
145
+ const isMultiSeries = clientNames.length > 0;
130
146
 
131
147
  return (
132
148
  <Card className="bg-white/5 border-white/10 backdrop-blur-md relative overflow-visible pt-6 pb-2">
133
149
  <button
134
150
  className="absolute top-0 right-0 z-20 h-8 px-3 rounded-tl-none rounded-tr-xl rounded-bl-2xl rounded-br-none bg-white/5 border-b border-l border-white/10 hover:bg-white/10 text-white text-xs font-medium flex items-center gap-2 transition-all group"
135
- onClick={() => document.getElementById(`dropdown-${service}`)?.classList.toggle('hidden')}
151
+ onClick={() =>
152
+ document
153
+ .getElementById(`dropdown-${service}`)
154
+ ?.classList.toggle("hidden")
155
+ }
136
156
  >
137
- <svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
157
+ <svg
158
+ className="w-3 h-3"
159
+ fill="none"
160
+ stroke="currentColor"
161
+ viewBox="0 0 24 24"
162
+ >
138
163
  <path
139
164
  strokeLinecap="round"
140
165
  strokeLinejoin="round"
@@ -147,13 +172,18 @@ export default function ChartCard({ service }: ChartCardProps) {
147
172
  </button>
148
173
 
149
174
  {/* Dropdown Menu - Positioned relative to the button or card */}
150
- <div id={`dropdown-${service}`} className="hidden absolute top-8 right-0 w-40 bg-[#0B1116]/95 border border-white/10 rounded-lg shadow-xl z-30 backdrop-blur-xl overflow-hidden">
175
+ <div
176
+ id={`dropdown-${service}`}
177
+ className="hidden absolute top-8 right-0 w-40 bg-[#0B1116]/95 border border-white/10 rounded-lg shadow-xl z-30 backdrop-blur-xl overflow-hidden"
178
+ >
151
179
  {periods.map((p) => (
152
180
  <button
153
181
  key={p.value}
154
182
  onClick={() => {
155
183
  setPeriod(p.value);
156
- document.getElementById(`dropdown-${service}`)?.classList.add('hidden');
184
+ document
185
+ .getElementById(`dropdown-${service}`)
186
+ ?.classList.add("hidden");
157
187
  }}
158
188
  className={`w-full text-left px-3 py-2 text-xs transition-colors ${
159
189
  period === p.value
@@ -169,29 +199,74 @@ export default function ChartCard({ service }: ChartCardProps) {
169
199
  <CardHeader className="relative">
170
200
  <div className="flex items-center justify-between">
171
201
  <div>
172
- <CardTitle className="text-sm font-medium text-slate-400">Time Series</CardTitle>
202
+ <CardTitle className="text-sm font-medium text-slate-400">
203
+ Time Series
204
+ </CardTitle>
173
205
  <div className="text-2xl font-bold text-white">{service}</div>
174
206
  </div>
175
207
  </div>
176
208
  </CardHeader>
177
209
  <CardContent>
178
210
  {loading ? (
211
+ <div className="h-[200px] w-full flex flex-col justify-end p-4">
212
+ {/* Skeleton chart bars */}
213
+ <div className="flex items-end justify-around h-full gap-2">
214
+ <div
215
+ className="w-8 bg-white/5 rounded-t animate-pulse"
216
+ style={{ height: "40%" }}
217
+ />
218
+ <div
219
+ className="w-8 bg-white/5 rounded-t animate-pulse"
220
+ style={{ height: "65%" }}
221
+ />
222
+ <div
223
+ className="w-8 bg-white/5 rounded-t animate-pulse"
224
+ style={{ height: "30%" }}
225
+ />
226
+ <div
227
+ className="w-8 bg-white/5 rounded-t animate-pulse"
228
+ style={{ height: "80%" }}
229
+ />
230
+ <div
231
+ className="w-8 bg-white/5 rounded-t animate-pulse"
232
+ style={{ height: "50%" }}
233
+ />
234
+ <div
235
+ className="w-8 bg-white/5 rounded-t animate-pulse"
236
+ style={{ height: "70%" }}
237
+ />
238
+ <div
239
+ className="w-8 bg-white/5 rounded-t animate-pulse"
240
+ style={{ height: "45%" }}
241
+ />
242
+ </div>
243
+ {/* Skeleton axis */}
244
+ <div className="h-px w-full bg-white/10 mt-2" />
245
+ </div>
246
+ ) : data.length === 0 || clientNames.length === 0 ? (
179
247
  <div className="h-[200px] w-full flex items-center justify-center">
180
- <div className="text-neutral-500 text-sm">Loading...</div>
248
+ <div className="text-center">
249
+ <div className="text-neutral-500 text-sm">
250
+ No data for this period
251
+ </div>
252
+ <div className="text-neutral-600 text-xs mt-1">
253
+ Try selecting a different time range
254
+ </div>
255
+ </div>
181
256
  </div>
182
257
  ) : (
183
258
  <div className="h-[200px] w-full">
184
259
  <MetricsChart
185
- data={data}
186
- isMultiSeries={isMultiSeries}
187
- clientNames={clientNames}
188
- clientConfigs={clientConfigs}
189
- hovered={hovered}
190
- setHovered={setHovered}
260
+ data={data}
261
+ isMultiSeries={isMultiSeries}
262
+ clientNames={clientNames}
263
+ clientConfigs={clientConfigs}
264
+ hovered={hovered}
265
+ setHovered={setHovered}
191
266
  />
192
267
  </div>
193
268
  )}
194
269
  </CardContent>
195
270
  </Card>
196
- )
271
+ );
197
272
  }
@@ -1,8 +1,15 @@
1
- "use client"
1
+ "use client";
2
2
 
3
3
  import { Button } from "@/components/ui/button";
4
4
  import { useDashboard } from "@/lib/data/dashboard-context";
5
- import { AlertCircle, Book, CheckCircle2, Loader2, Settings, XCircle } from "lucide-react";
5
+ import {
6
+ AlertCircle,
7
+ Book,
8
+ CheckCircle2,
9
+ Loader2,
10
+ Settings,
11
+ XCircle,
12
+ } from "lucide-react";
6
13
  import Image from "next/image";
7
14
  import { useEffect, useState } from "react";
8
15
 
@@ -13,53 +20,87 @@ export default function DashboardHeader({
13
20
  onShowMCPConfig: () => void;
14
21
  onShowSettings: () => void;
15
22
  }) {
16
- const [isScrolled, setIsScrolled] = useState(false)
17
- const [showHealthPopup, setShowHealthPopup] = useState(false)
18
- const { systemHealth } = useDashboard()
23
+ const [isScrolled, setIsScrolled] = useState(false);
24
+ const [showHealthPopup, setShowHealthPopup] = useState(false);
25
+ const { systemHealth } = useDashboard();
19
26
 
20
27
  useEffect(() => {
21
28
  // Check initial scroll position on mount
22
- setIsScrolled(window.scrollY > 10)
29
+ setIsScrolled(window.scrollY > 10);
23
30
 
24
31
  const handleScroll = () => {
25
- setIsScrolled(window.scrollY > 10)
26
- }
27
- window.addEventListener("scroll", handleScroll)
28
- return () => window.removeEventListener("scroll", handleScroll)
29
- }, [])
32
+ setIsScrolled(window.scrollY > 10);
33
+ };
34
+ window.addEventListener("scroll", handleScroll);
35
+ return () => window.removeEventListener("scroll", handleScroll);
36
+ }, []);
30
37
 
31
38
  const getStatusConfig = () => {
32
39
  if (!systemHealth) {
33
- return { bg: "bg-neutral-500/10", text: "text-neutral-400", border: "border-neutral-500/20", icon: Loader2, label: "Checking..." }
40
+ return {
41
+ bg: "bg-neutral-500/10",
42
+ text: "text-neutral-400",
43
+ border: "border-neutral-500/20",
44
+ icon: Loader2,
45
+ label: "Checking...",
46
+ };
34
47
  }
35
48
  switch (systemHealth.overall) {
36
- case 'ok':
37
- return { bg: "bg-emerald-500/10", text: "text-emerald-400", border: "border-emerald-500/20", icon: CheckCircle2, label: "All Systems OK" }
38
- case 'degraded':
39
- return { bg: "bg-amber-500/10", text: "text-amber-400", border: "border-amber-500/20", icon: AlertCircle, label: "Degraded" }
40
- case 'error':
41
- return { bg: "bg-red-500/10", text: "text-red-400", border: "border-red-500/20", icon: XCircle, label: "System Error" }
49
+ case "ok":
50
+ return {
51
+ bg: "bg-emerald-500/10",
52
+ text: "text-emerald-400",
53
+ border: "border-emerald-500/20",
54
+ icon: CheckCircle2,
55
+ label: "All Systems OK",
56
+ };
57
+ case "degraded":
58
+ return {
59
+ bg: "bg-amber-500/10",
60
+ text: "text-amber-400",
61
+ border: "border-amber-500/20",
62
+ icon: AlertCircle,
63
+ label: "Degraded",
64
+ };
65
+ case "error":
66
+ return {
67
+ bg: "bg-red-500/10",
68
+ text: "text-red-400",
69
+ border: "border-red-500/20",
70
+ icon: XCircle,
71
+ label: "System Error",
72
+ };
42
73
  }
43
- }
74
+ };
44
75
 
45
- const statusConfig = getStatusConfig()
46
- const StatusIcon = statusConfig.icon
76
+ const statusConfig = getStatusConfig();
77
+ const StatusIcon = statusConfig.icon;
47
78
 
48
79
  return (
49
- <header className={`sticky top-0 z-50 transition-all duration-300 ${
50
- isScrolled
51
- ? "border-b border-white/10 backdrop-blur-xl bg-neutral-900/30"
52
- : "border-b border-transparent bg-transparent"
53
- }`}>
80
+ <header
81
+ className={`sticky top-0 z-50 transition-all duration-300 ${
82
+ isScrolled
83
+ ? "border-b border-white/10 backdrop-blur-xl bg-neutral-900/30"
84
+ : "border-b border-transparent bg-transparent"
85
+ }`}
86
+ >
54
87
  <div className="px-6 py-5 max-w-7xl mx-auto">
55
88
  <div className="flex items-center justify-between">
56
89
  <div className="flex items-center gap-4">
57
90
  <div className="relative w-10 h-10 flex-shrink-0">
58
- <Image src="/logo.svg" alt="CyberMem Logo" width={40} height={40} className="object-contain" />
91
+ <Image
92
+ src="/logo.svg"
93
+ alt="CyberMem Logo"
94
+ width={40}
95
+ height={40}
96
+ className="object-contain"
97
+ />
59
98
  </div>
60
99
  <div>
61
100
  <div className="flex items-center gap-3">
62
- <h1 className="text-3xl font-bold tracking-tight text-white leading-none font-exo">CyberMem</h1>
101
+ <h1 className="text-3xl font-bold tracking-tight text-white leading-none font-exo">
102
+ CyberMem
103
+ </h1>
63
104
 
64
105
  {/* System Health Status Badge with Hover Popup */}
65
106
  <div
@@ -67,29 +108,55 @@ export default function DashboardHeader({
67
108
  onMouseEnter={() => setShowHealthPopup(true)}
68
109
  onMouseLeave={() => setShowHealthPopup(false)}
69
110
  >
70
- <div className={`px-2 py-[2px] rounded-full text-[10px] font-medium flex items-center gap-1 cursor-pointer ${statusConfig.bg} ${statusConfig.text} border ${statusConfig.border}`}>
71
- <StatusIcon className={`w-3 h-3 ${!systemHealth ? 'animate-spin' : ''}`} />
72
- {statusConfig.label}
73
- </div>
111
+ {!systemHealth ? (
112
+ /* Shimmer loading state */
113
+ <div className="px-3 py-[2px] rounded-full bg-white/5 border border-white/10 animate-pulse">
114
+ <div className="flex items-center gap-1">
115
+ <div className="w-3 h-3 rounded-full bg-white/10" />
116
+ <div className="w-16 h-3 rounded bg-white/10" />
117
+ </div>
118
+ </div>
119
+ ) : (
120
+ <div
121
+ className={`px-2 py-[2px] rounded-full text-[10px] font-medium flex items-center gap-1 cursor-pointer ${statusConfig.bg} ${statusConfig.text} border ${statusConfig.border}`}
122
+ >
123
+ <StatusIcon className="w-3 h-3" />
124
+ {statusConfig.label}
125
+ </div>
126
+ )}
74
127
 
75
128
  {/* Hover Popup */}
76
129
  {showHealthPopup && systemHealth && (
77
130
  <div className="absolute top-full left-0 mt-2 w-64 bg-[#0B1116]/95 border border-white/10 rounded-lg shadow-xl z-50 backdrop-blur-xl overflow-hidden animate-in fade-in slide-in-from-top-2 duration-200">
78
131
  <div className="p-3 border-b border-white/5">
79
- <p className="text-xs text-neutral-400">System Health</p>
80
- <p className="text-[10px] text-neutral-500 mt-0.5">Updated: {new Date(systemHealth.timestamp).toLocaleTimeString()}</p>
132
+ <p className="text-xs text-neutral-400">
133
+ System Health
134
+ </p>
135
+ <p className="text-[10px] text-neutral-500 mt-0.5">
136
+ Updated:{" "}
137
+ {new Date(
138
+ systemHealth.timestamp,
139
+ ).toLocaleTimeString()}
140
+ </p>
81
141
  </div>
82
142
  <div className="p-2 space-y-1">
83
143
  {systemHealth.services.map((service, i) => (
84
- <div key={i} className="flex items-center justify-between px-2 py-1.5 rounded hover:bg-white/5">
85
- <span className="text-xs text-neutral-300">{service.name}</span>
144
+ <div
145
+ key={i}
146
+ className="flex items-center justify-between px-2 py-1.5 rounded hover:bg-white/5"
147
+ >
148
+ <span className="text-xs text-neutral-300">
149
+ {service.name}
150
+ </span>
86
151
  <div className="flex items-center gap-2">
87
152
  {service.latencyMs && (
88
- <span className="text-[10px] text-neutral-500">{service.latencyMs}ms</span>
153
+ <span className="text-[10px] text-neutral-500">
154
+ {service.latencyMs}ms
155
+ </span>
89
156
  )}
90
- {service.status === 'ok' ? (
157
+ {service.status === "ok" ? (
91
158
  <CheckCircle2 className="w-3 h-3 text-emerald-400" />
92
- ) : service.status === 'warning' ? (
159
+ ) : service.status === "warning" ? (
93
160
  <AlertCircle className="w-3 h-3 text-amber-400" />
94
161
  ) : (
95
162
  <XCircle className="w-3 h-3 text-red-400" />
@@ -97,12 +164,23 @@ export default function DashboardHeader({
97
164
  </div>
98
165
  </div>
99
166
  ))}
100
- {systemHealth.services.some(s => s.status !== 'ok' && s.message) && (
167
+ {systemHealth.services.some(
168
+ (s) => s.status !== "ok" && s.message,
169
+ ) && (
101
170
  <div className="mt-2 p-2 rounded bg-red-500/10 border border-red-500/20">
102
- <p className="text-xs text-red-300 font-medium">Issues:</p>
103
- {systemHealth.services.filter(s => s.status !== 'ok' && s.message).map((s, i) => (
104
- <p key={i} className="text-[10px] text-red-400 mt-1">• {s.name}: {s.message}</p>
105
- ))}
171
+ <p className="text-xs text-red-300 font-medium">
172
+ Issues:
173
+ </p>
174
+ {systemHealth.services
175
+ .filter((s) => s.status !== "ok" && s.message)
176
+ .map((s, i) => (
177
+ <p
178
+ key={i}
179
+ className="text-[10px] text-red-400 mt-1"
180
+ >
181
+ • {s.name}: {s.message}
182
+ </p>
183
+ ))}
106
184
  </div>
107
185
  )}
108
186
  </div>
@@ -112,19 +190,22 @@ export default function DashboardHeader({
112
190
  </div>
113
191
  <p className="text-sm text-neutral-400 mt-1">Memory MCP Server</p>
114
192
  </div>
115
-
116
193
  </div>
117
194
 
118
195
  <div className="flex items-center gap-3">
119
-
120
-
121
196
  <Button
122
197
  variant="ghost"
123
198
  size="sm"
124
199
  onClick={onShowMCPConfig}
125
200
  className="hidden md:flex h-10 px-4 text-sm font-medium bg-emerald-500/10 text-emerald-400 hover:text-emerald-300 hover:bg-emerald-500/20 border border-emerald-500/20 hover:border-emerald-500/40 rounded-lg"
126
201
  >
127
- <Image src="/icons/mcp.png" alt="MCP" width={16} height={16} className="mr-2" />
202
+ <Image
203
+ src="/icons/mcp.png"
204
+ alt="MCP"
205
+ width={16}
206
+ height={16}
207
+ className="mr-2"
208
+ />
128
209
  Connect MCP
129
210
  </Button>
130
211
 
@@ -134,7 +215,7 @@ export default function DashboardHeader({
134
215
  asChild
135
216
  className="hidden md:flex h-10 px-4 text-sm font-medium text-neutral-400 hover:text-white bg-white/5 border border-white/10 hover:bg-white/10 rounded-lg"
136
217
  >
137
- <a href="https://cybermem.dev/docs" target="_blank">
218
+ <a href="https://docs.cybermem.dev" target="_blank">
138
219
  <Book className="w-4 h-4 mr-2" />
139
220
  Docs
140
221
  </a>
@@ -152,5 +233,5 @@ export default function DashboardHeader({
152
233
  </div>
153
234
  </div>
154
235
  </header>
155
- )
236
+ );
156
237
  }
@@ -423,7 +423,7 @@ export default function MCPConfigModal({ onClose }: { onClose: () => void }) {
423
423
  variant="ghost"
424
424
  className="bg-emerald-500/10 hover:bg-emerald-500/20 border border-emerald-500/20 text-emerald-400 hover:text-emerald-300 mr-auto"
425
425
  >
426
- <a href="https://cybermem.dev/docs" target="_blank">Read Documentation</a>
426
+ <a href="https://docs.cybermem.dev" target="_blank">Read Documentation</a>
427
427
  </Button>
428
428
  <Button
429
429
  onClick={onClose}