@sleep2agi/agent-network-dashboard 0.4.5-preview.0 → 0.4.5-preview.10
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/.next/BUILD_ID +1 -1
- package/.next/build-manifest.json +3 -3
- package/.next/diagnostics/route-bundle-stats.json +37 -37
- package/.next/fallback-build-manifest.json +3 -3
- package/.next/prerender-manifest.json +3 -3
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +2 -2
- package/.next/server/app/_not-found.rsc +12 -12
- package/.next/server/app/_not-found.segments/_full.segment.rsc +12 -12
- package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/server/app/_not-found.segments/_index.segment.rsc +7 -7
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/admin/page.js.nft.json +1 -1
- package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
- package/.next/server/app/admin.html +2 -2
- package/.next/server/app/admin.rsc +14 -14
- package/.next/server/app/admin.segments/_full.segment.rsc +14 -14
- package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
- package/.next/server/app/admin.segments/_index.segment.rsc +7 -7
- package/.next/server/app/admin.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/admin.segments/admin.segment.rsc +3 -3
- package/.next/server/app/index.html +2 -2
- package/.next/server/app/index.rsc +14 -14
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/index.segments/_full.segment.rsc +14 -14
- package/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/server/app/index.segments/_index.segment.rsc +7 -7
- package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login/page.js.nft.json +1 -1
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/login.html +2 -2
- package/.next/server/app/login.rsc +14 -14
- package/.next/server/app/login.segments/_full.segment.rsc +14 -14
- package/.next/server/app/login.segments/_head.segment.rsc +4 -4
- package/.next/server/app/login.segments/_index.segment.rsc +7 -7
- package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/login.segments/login.segment.rsc +3 -3
- package/.next/server/app/logs/page.js.nft.json +1 -1
- package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/logs.html +2 -2
- package/.next/server/app/logs.rsc +14 -14
- package/.next/server/app/logs.segments/_full.segment.rsc +14 -14
- package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
- package/.next/server/app/logs.segments/_index.segment.rsc +7 -7
- package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
- package/.next/server/app/messages/page.js.nft.json +1 -1
- package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
- package/.next/server/app/messages.html +2 -2
- package/.next/server/app/messages.rsc +14 -14
- package/.next/server/app/messages.segments/_full.segment.rsc +14 -14
- package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
- package/.next/server/app/messages.segments/_index.segment.rsc +7 -7
- package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
- package/.next/server/app/node/page.js.nft.json +1 -1
- package/.next/server/app/node/page_client-reference-manifest.js +1 -1
- package/.next/server/app/node.html +2 -2
- package/.next/server/app/node.rsc +14 -14
- package/.next/server/app/node.segments/_full.segment.rsc +14 -14
- package/.next/server/app/node.segments/_head.segment.rsc +4 -4
- package/.next/server/app/node.segments/_index.segment.rsc +7 -7
- package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/node.segments/node.segment.rsc +3 -3
- package/.next/server/app/nodes/page.js.nft.json +1 -1
- package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
- package/.next/server/app/nodes.html +2 -2
- package/.next/server/app/nodes.rsc +14 -14
- package/.next/server/app/nodes.segments/_full.segment.rsc +14 -14
- package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
- package/.next/server/app/nodes.segments/_index.segment.rsc +7 -7
- package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
- package/.next/server/app/page.js.nft.json +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/server-logs/page.js.nft.json +1 -1
- package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/server-logs.html +2 -2
- package/.next/server/app/server-logs.rsc +14 -14
- package/.next/server/app/server-logs.segments/_full.segment.rsc +14 -14
- package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
- package/.next/server/app/server-logs.segments/_index.segment.rsc +7 -7
- package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
- package/.next/server/app/settings/networks/page.js.nft.json +1 -1
- package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/networks.html +2 -2
- package/.next/server/app/settings/networks.rsc +14 -14
- package/.next/server/app/settings/networks.segments/_full.segment.rsc +14 -14
- package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings/networks.segments/_index.segment.rsc +7 -7
- package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
- package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
- package/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/tokens/page.js.nft.json +1 -1
- package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/tokens.html +2 -2
- package/.next/server/app/settings/tokens.rsc +14 -14
- package/.next/server/app/settings/tokens.segments/_full.segment.rsc +14 -14
- package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings/tokens.segments/_index.segment.rsc +7 -7
- package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
- package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
- package/.next/server/app/settings.html +2 -2
- package/.next/server/app/settings.rsc +14 -14
- package/.next/server/app/settings.segments/_full.segment.rsc +14 -14
- package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings.segments/_index.segment.rsc +7 -7
- package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
- package/.next/server/app/tasks/[id]/page.js.nft.json +1 -1
- package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/tasks/page.js.nft.json +1 -1
- package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
- package/.next/server/app/tasks.html +2 -2
- package/.next/server/app/tasks.rsc +14 -14
- package/.next/server/app/tasks.segments/_full.segment.rsc +14 -14
- package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
- package/.next/server/app/tasks.segments/_index.segment.rsc +7 -7
- package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0a0ffmv._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0a0ffmv._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0epcm6p._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0epcm6p._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0to~xqw._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0to~xqw._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0xm3h9x._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0xm3h9x._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0xo8x6b._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0xo8x6b._.js.map +1 -0
- package/.next/server/chunks/ssr/_13_v7-3._.js +3 -0
- package/.next/server/chunks/ssr/_13_v7-3._.js.map +1 -0
- package/.next/server/chunks/ssr/app_10b5blc._.js +9 -0
- package/.next/server/chunks/ssr/app_10b5blc._.js.map +1 -0
- package/.next/server/chunks/ssr/app_components_08ys5y3._.js +9 -0
- package/.next/server/chunks/ssr/app_components_08ys5y3._.js.map +1 -0
- package/.next/server/chunks/ssr/app_page_tsx_0es_sk2._.js +1 -1
- package/.next/server/chunks/ssr/app_page_tsx_0es_sk2._.js.map +1 -1
- package/.next/server/chunks/ssr/app_tasks_page_tsx_0z-d6mp._.js +1 -1
- package/.next/server/chunks/ssr/app_tasks_page_tsx_0z-d6mp._.js.map +1 -1
- package/.next/server/middleware-build-manifest.js +3 -3
- package/.next/server/pages/404.html +2 -2
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.js +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/03wwuo3wor.05.js +1 -0
- package/.next/static/chunks/04nx85kn48m6a.css +2 -0
- package/.next/static/chunks/09xm65~ucg19e.js +1 -0
- package/.next/static/chunks/0bbx5m_hcs39u.js +1 -0
- package/.next/static/chunks/0f0tgtns1m6zh.js +1 -0
- package/.next/static/chunks/10ir.g.n~hre0.js +7 -0
- package/.next/static/chunks/12dcb86elsl-e.js +7 -0
- package/.next/static/chunks/12xk2ohuxsyo5.js +1 -0
- package/.next/static/chunks/132e__998_q9r.js +1 -0
- package/.next/trace +2 -2
- package/.next/trace-build +1 -1
- package/app/components/AgentCard.tsx +6 -2
- package/app/components/AliasAvatar.tsx +51 -0
- package/app/components/AppShell.tsx +9 -1
- package/app/components/CommandPalette.tsx +391 -0
- package/app/components/HealthBanner.tsx +132 -0
- package/app/components/HelpOverlay.tsx +121 -0
- package/app/components/LoadingSkeleton.tsx +81 -25
- package/app/components/Sidebar.tsx +18 -0
- package/app/components/TaskChatPanel.tsx +7 -5
- package/app/components/TopoGraph.tsx +16 -3
- package/app/components/UserBar.tsx +16 -2
- package/app/globals.css +244 -0
- package/app/messages/page.tsx +79 -48
- package/app/nodes/page.tsx +21 -23
- package/app/page.tsx +96 -20
- package/app/tasks/page.tsx +130 -40
- package/docs/loop-design-rounds.md +298 -3
- package/docs/screenshots/0.4.5-preview.1/cyber-admin.png +0 -0
- package/docs/screenshots/0.4.5-preview.1/cyber-cmdk-search.png +0 -0
- package/docs/screenshots/0.4.5-preview.1/cyber-cmdk.png +0 -0
- package/docs/screenshots/0.4.5-preview.1/cyber-login.png +0 -0
- package/docs/screenshots/0.4.5-preview.1/cyber-overview.png +0 -0
- package/docs/screenshots/0.4.5-preview.1/cyber-settings.png +0 -0
- package/docs/screenshots/0.4.5-preview.1/cyber-tasks.png +0 -0
- package/docs/screenshots/0.4.5-preview.1/light-admin.png +0 -0
- package/docs/screenshots/0.4.5-preview.1/light-cmdk-search.png +0 -0
- package/docs/screenshots/0.4.5-preview.1/light-cmdk.png +0 -0
- package/docs/screenshots/0.4.5-preview.1/light-login.png +0 -0
- package/docs/screenshots/0.4.5-preview.1/light-overview.png +0 -0
- package/docs/screenshots/0.4.5-preview.1/light-settings.png +0 -0
- package/docs/screenshots/0.4.5-preview.1/light-tasks.png +0 -0
- package/package.json +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__013i9e9._.js +0 -9
- package/.next/server/chunks/ssr/[root-of-the-server]__013i9e9._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0_jnflc._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0_jnflc._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0nrckjd._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0nrckjd._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__12mpoig._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__12mpoig._.js.map +0 -1
- package/.next/server/chunks/ssr/_0d-fuox._.js +0 -3
- package/.next/server/chunks/ssr/_0d-fuox._.js.map +0 -1
- package/.next/server/chunks/ssr/app_0ff~kjj._.js +0 -9
- package/.next/server/chunks/ssr/app_0ff~kjj._.js.map +0 -1
- package/.next/static/chunks/06t_vzei8s9_h.js +0 -1
- package/.next/static/chunks/07okhs5561y5y.js +0 -1
- package/.next/static/chunks/0hyhubmh-__wg.js +0 -1
- package/.next/static/chunks/0khpyxpdrp~ty.js +0 -1
- package/.next/static/chunks/0lo_g.q1fm-hr.js +0 -7
- package/.next/static/chunks/0ogeals5ufhho.js +0 -1
- package/.next/static/chunks/0snd~q2ucn.h1.css +0 -2
- package/.next/static/chunks/0xn9.0ejo1s_a.js +0 -7
- package/.next/static/chunks/0y0lo-~k2c~00.js +0 -1
- /package/.next/static/{2m1yIfZCIYDWMYrrJEKSC → kfcVh72WCbkWVFpnWl0vE}/_buildManifest.js +0 -0
- /package/.next/static/{2m1yIfZCIYDWMYrrJEKSC → kfcVh72WCbkWVFpnWl0vE}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{2m1yIfZCIYDWMYrrJEKSC → kfcVh72WCbkWVFpnWl0vE}/_ssgManifest.js +0 -0
package/app/messages/page.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import { useMemo, useState } from 'react';
|
|
|
4
4
|
import { useMessages } from '../lib/hooks';
|
|
5
5
|
import { timeAgo } from '../components/utils';
|
|
6
6
|
import { EmptyState } from '../components/EmptyState';
|
|
7
|
+
import { AliasAvatar } from '../components/AliasAvatar';
|
|
7
8
|
|
|
8
9
|
interface MessageItem {
|
|
9
10
|
id: string;
|
|
@@ -40,6 +41,17 @@ function bubbleVariant(message: MessageItem) {
|
|
|
40
41
|
return 'incoming';
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
/** Render message content, highlighting the search match inline (skips the
|
|
45
|
+
* `from:` filter prefix since it filters by sender, not by content). */
|
|
46
|
+
function renderHighlighted(content: string | undefined, search: string) {
|
|
47
|
+
const text = content || '--';
|
|
48
|
+
const q = search.trim();
|
|
49
|
+
if (!q || q.startsWith('from:')) return text;
|
|
50
|
+
const idx = text.toLowerCase().indexOf(q.toLowerCase());
|
|
51
|
+
if (idx < 0) return text;
|
|
52
|
+
return <>{text.slice(0, idx)}<mark className="bg-yellow-500/30 text-yellow-200 rounded px-0.5">{text.slice(idx, idx + q.length)}</mark>{text.slice(idx + q.length)}</>;
|
|
53
|
+
}
|
|
54
|
+
|
|
43
55
|
export default function MessagesPage() {
|
|
44
56
|
const { messages, isLoading } = useMessages(200);
|
|
45
57
|
const [filterType, setFilterType] = useState('');
|
|
@@ -213,13 +225,21 @@ export default function MessagesPage() {
|
|
|
213
225
|
))}
|
|
214
226
|
</div>
|
|
215
227
|
) : (
|
|
216
|
-
<div className="space-y-
|
|
228
|
+
<div className="space-y-1">
|
|
217
229
|
{filtered.map((message: MessageItem, index) => {
|
|
218
230
|
const previous = filtered[index - 1];
|
|
219
231
|
const gapExceeded = previous
|
|
220
232
|
? normalizeDate(message.created_at) - normalizeDate(previous.created_at) > 5 * 60 * 1000
|
|
221
233
|
: false;
|
|
222
234
|
const variant = bubbleVariant(message);
|
|
235
|
+
// Cluster: same sender + same direction + same to_alias and no
|
|
236
|
+
// time-gap divider → tighter spacing and hide repeated header.
|
|
237
|
+
const samePrev = !!previous
|
|
238
|
+
&& !gapExceeded
|
|
239
|
+
&& bubbleVariant(previous) === variant
|
|
240
|
+
&& (previous.from_alias || '') === (message.from_alias || '')
|
|
241
|
+
&& (previous.to_alias || '') === (message.to_alias || '');
|
|
242
|
+
const fromAlias = message.from_alias || '?';
|
|
223
243
|
|
|
224
244
|
return (
|
|
225
245
|
<div key={message.id}>
|
|
@@ -231,60 +251,71 @@ export default function MessagesPage() {
|
|
|
231
251
|
</div>
|
|
232
252
|
)}
|
|
233
253
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
254
|
+
{/* Broadcasts span full width — no avatar gutter. */}
|
|
255
|
+
{variant === 'broadcast' ? (
|
|
256
|
+
<div className={samePrev ? 'mt-1' : 'mt-3'}>
|
|
257
|
+
<div className="rounded-2xl border border-purple-500/20 bg-purple-500/10 px-4 py-3 shadow-sm w-full">
|
|
258
|
+
<div className="mb-2 flex flex-wrap items-center gap-2">
|
|
259
|
+
<span className={`text-xs px-2 py-0.5 rounded-md border ${TYPE_COLORS[message.type || ''] || 'bg-gray-500/10 text-gray-400 border-gray-500/20'}`}>
|
|
260
|
+
{message.type || 'unknown'}
|
|
261
|
+
</span>
|
|
262
|
+
<span className="text-sm font-medium text-purple-200">{fromAlias}</span>
|
|
263
|
+
{message.to_alias && <span className="text-xs text-purple-200/80">to {message.to_alias}</span>}
|
|
264
|
+
{message.priority === 'high' && <span className="text-xs text-red-400">HIGH</span>}
|
|
265
|
+
<span className="ml-auto text-xs text-gray-500">{message.created_at ? timeAgo(message.created_at) : '--'}</span>
|
|
266
|
+
</div>
|
|
267
|
+
<div className="whitespace-pre-wrap text-sm leading-relaxed text-gray-200">
|
|
268
|
+
{renderHighlighted(message.content, search)}
|
|
269
|
+
</div>
|
|
270
|
+
</div>
|
|
271
|
+
</div>
|
|
272
|
+
) : (
|
|
273
|
+
<div className={`${samePrev ? 'mt-1' : 'mt-3'} flex gap-2 ${variant === 'outgoing' ? 'flex-row-reverse' : 'flex-row'}`}>
|
|
274
|
+
{/* Avatar gutter — fixed width keeps bubble columns aligned even on streaks */}
|
|
275
|
+
<div className="w-8 shrink-0 pt-1">
|
|
276
|
+
{!samePrev && <AliasAvatar alias={fromAlias} size={32} />}
|
|
277
|
+
</div>
|
|
278
|
+
<div className={`min-w-0 max-w-3xl rounded-2xl border px-4 py-3 shadow-sm ${
|
|
279
|
+
variant === 'outgoing'
|
|
280
|
+
? 'border-green-500/20 bg-green-500/10'
|
|
281
|
+
: 'border-blue-500/20 bg-blue-500/10'
|
|
282
|
+
}`}>
|
|
283
|
+
{!samePrev && (
|
|
284
|
+
<div className="mb-2 flex flex-wrap items-center gap-2">
|
|
285
|
+
<span className={`text-xs px-2 py-0.5 rounded-md border ${TYPE_COLORS[message.type || ''] || 'bg-gray-500/10 text-gray-400 border-gray-500/20'}`}>
|
|
286
|
+
{message.type || 'unknown'}
|
|
287
|
+
</span>
|
|
288
|
+
<span className={`text-sm font-medium ${variant === 'outgoing' ? 'text-green-300' : 'text-blue-300'}`}>
|
|
289
|
+
{fromAlias}
|
|
290
|
+
</span>
|
|
257
291
|
<span className="text-xs text-gray-600">→</span>
|
|
258
292
|
<span className="text-sm text-gray-300">{message.to_alias || '--'}</span>
|
|
259
|
-
|
|
293
|
+
{message.priority === 'high' && <span className="text-xs text-red-400">HIGH</span>}
|
|
294
|
+
<span className="ml-auto text-xs text-gray-500">{message.created_at ? timeAgo(message.created_at) : '--'}</span>
|
|
295
|
+
</div>
|
|
260
296
|
)}
|
|
261
|
-
|
|
262
|
-
|
|
297
|
+
|
|
298
|
+
<div className="whitespace-pre-wrap text-sm leading-relaxed text-gray-200">
|
|
299
|
+
{renderHighlighted(message.content, search)}
|
|
300
|
+
</div>
|
|
301
|
+
|
|
302
|
+
{/* When in a streak, surface the timestamp inline-right so users still know cadence */}
|
|
303
|
+
{samePrev && (
|
|
304
|
+
<div className="mt-1 text-[10px] text-gray-600 text-right">
|
|
305
|
+
{message.created_at ? timeAgo(message.created_at) : ''}
|
|
306
|
+
</div>
|
|
263
307
|
)}
|
|
264
|
-
{message.priority === 'high' && <span className="text-xs text-red-400">HIGH</span>}
|
|
265
|
-
<span className="ml-auto text-xs text-gray-500">{message.created_at ? timeAgo(message.created_at) : '--'}</span>
|
|
266
|
-
</div>
|
|
267
308
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
return <>{text.slice(0, idx)}<mark className="bg-yellow-500/30 text-yellow-200 rounded px-0.5">{text.slice(idx, idx + q.length)}</mark>{text.slice(idx + q.length)}</>;
|
|
276
|
-
})()}
|
|
309
|
+
{debug && (
|
|
310
|
+
<div className="mt-3 border-t border-white/10 pt-3 text-xs text-gray-500 space-y-1">
|
|
311
|
+
<div>ID: {message.id}</div>
|
|
312
|
+
{message.task_id && <div>Task ID: {message.task_id}</div>}
|
|
313
|
+
<div>Created: {message.created_at}</div>
|
|
314
|
+
</div>
|
|
315
|
+
)}
|
|
277
316
|
</div>
|
|
278
|
-
|
|
279
|
-
{debug && (
|
|
280
|
-
<div className="mt-3 border-t border-white/10 pt-3 text-xs text-gray-500 space-y-1">
|
|
281
|
-
<div>ID: {message.id}</div>
|
|
282
|
-
{message.task_id && <div>Task ID: {message.task_id}</div>}
|
|
283
|
-
<div>Created: {message.created_at}</div>
|
|
284
|
-
</div>
|
|
285
|
-
)}
|
|
286
317
|
</div>
|
|
287
|
-
|
|
318
|
+
)}
|
|
288
319
|
</div>
|
|
289
320
|
);
|
|
290
321
|
})}
|
package/app/nodes/page.tsx
CHANGED
|
@@ -6,6 +6,7 @@ import { timeAgo } from '../components/utils';
|
|
|
6
6
|
import { useSessions, useHealth } from '../lib/hooks';
|
|
7
7
|
import { TaskChatPanel } from '../components/TaskChatPanel';
|
|
8
8
|
import { EmptyState } from '../components/EmptyState';
|
|
9
|
+
import { AliasAvatar } from '../components/AliasAvatar';
|
|
9
10
|
import type { Session } from '../components/types';
|
|
10
11
|
|
|
11
12
|
const STATUS_COLORS: Record<string, string> = {
|
|
@@ -188,14 +189,18 @@ export default function NodesPage() {
|
|
|
188
189
|
</span>
|
|
189
190
|
</div>
|
|
190
191
|
)}
|
|
191
|
-
<div className="flex items-start
|
|
192
|
+
<div className="flex items-start gap-3">
|
|
193
|
+
<AliasAvatar alias={s.alias} size={36} />
|
|
192
194
|
<Link href={`/node?alias=${encodeURIComponent(s.alias)}`} className="min-w-0 flex-1 hover:text-cyan-300">
|
|
193
195
|
<div className="flex items-center gap-2">
|
|
194
196
|
<span className="truncate text-base font-semibold text-white">{s.alias}</span>
|
|
195
|
-
<span className={`shrink-0 rounded-md border px-2 py-0.5 text-[
|
|
197
|
+
<span className={`shrink-0 rounded-md border px-2 py-0.5 text-[10px] uppercase tracking-wide ${STATUS_COLORS[statusKey] || STATUS_COLORS.offline}`}>
|
|
196
198
|
{statusKey}
|
|
197
199
|
</span>
|
|
198
200
|
</div>
|
|
201
|
+
<div className="mt-0.5 truncate text-xs text-gray-500">
|
|
202
|
+
{(s.agent || '—')}<span className="text-gray-700 mx-1.5">·</span>{(s.server || '—')}
|
|
203
|
+
</div>
|
|
199
204
|
</Link>
|
|
200
205
|
<div className="flex gap-1.5 shrink-0">
|
|
201
206
|
<button type="button" onClick={() => setChatAlias(s.alias)}
|
|
@@ -209,25 +214,16 @@ export default function NodesPage() {
|
|
|
209
214
|
</div>
|
|
210
215
|
</div>
|
|
211
216
|
|
|
212
|
-
<div className="mt-3
|
|
213
|
-
<div className="flex justify-between gap-3">
|
|
214
|
-
<span>
|
|
215
|
-
<span className="
|
|
216
|
-
</div>
|
|
217
|
-
<div className="flex justify-between gap-3">
|
|
218
|
-
<span>Server</span>
|
|
219
|
-
<span className="truncate text-gray-300">{s.server || '--'}</span>
|
|
217
|
+
<div className="mt-3 rounded-lg border border-[#1a1a2a] bg-[#0a0a15] px-3 py-2 text-xs">
|
|
218
|
+
<div className="flex items-center justify-between gap-3">
|
|
219
|
+
<span className="text-[10px] uppercase tracking-wide text-gray-600">Current task</span>
|
|
220
|
+
<span className="text-[10px] text-gray-600">{timeAgo(s.last_seen_at || s.updated_at)}</span>
|
|
220
221
|
</div>
|
|
221
|
-
<div className=
|
|
222
|
-
|
|
223
|
-
<span className="text-gray-300">{timeAgo(s.last_seen_at || s.updated_at)}</span>
|
|
222
|
+
<div className={`mt-1 line-clamp-2 ${s.task ? 'text-gray-300' : 'text-gray-600 italic'}`}>
|
|
223
|
+
{s.task || 'No current task'}
|
|
224
224
|
</div>
|
|
225
225
|
</div>
|
|
226
226
|
|
|
227
|
-
<div className="mt-3 rounded-lg border border-[#1a1a2a] bg-[#0a0a15] px-3 py-2 text-xs text-gray-400">
|
|
228
|
-
{s.task ? s.task : 'No current task'}
|
|
229
|
-
</div>
|
|
230
|
-
|
|
231
227
|
{progress > 0 && progress < 100 && (
|
|
232
228
|
<div className="mt-3 h-1.5 overflow-hidden rounded-full bg-gray-800">
|
|
233
229
|
<div className="h-1.5 rounded-full bg-cyan-400 transition-all" style={{ width: `${progress}%` }} />
|
|
@@ -297,8 +293,9 @@ export default function NodesPage() {
|
|
|
297
293
|
</span>
|
|
298
294
|
</div>
|
|
299
295
|
<div className="col-span-2 min-w-0">
|
|
300
|
-
<Link href={`/node?alias=${encodeURIComponent(s.alias)}`} className="
|
|
301
|
-
{s.alias}
|
|
296
|
+
<Link href={`/node?alias=${encodeURIComponent(s.alias)}`} className="flex items-center gap-2 min-w-0 hover:text-cyan-300">
|
|
297
|
+
<AliasAvatar alias={s.alias} size={20} />
|
|
298
|
+
<span className="truncate text-sm font-medium text-white">{s.alias}</span>
|
|
302
299
|
</Link>
|
|
303
300
|
</div>
|
|
304
301
|
<div className="col-span-2 truncate text-xs text-gray-400">{s.agent || '--'}</div>
|
|
@@ -317,15 +314,16 @@ export default function NodesPage() {
|
|
|
317
314
|
</div>
|
|
318
315
|
</div>
|
|
319
316
|
<div className="sm:hidden space-y-2">
|
|
320
|
-
<div className="flex items-center
|
|
321
|
-
<
|
|
322
|
-
|
|
317
|
+
<div className="flex items-center gap-2.5">
|
|
318
|
+
<AliasAvatar alias={s.alias} size={28} />
|
|
319
|
+
<Link href={`/node?alias=${encodeURIComponent(s.alias)}`} className="min-w-0 flex-1 hover:text-cyan-300">
|
|
320
|
+
<div className="truncate text-sm font-medium text-white">{s.alias}</div>
|
|
321
|
+
<div className="truncate text-[10px] text-gray-500">{s.agent || '—'} · {timeAgo(s.last_seen_at || s.updated_at)}</div>
|
|
323
322
|
</Link>
|
|
324
323
|
<span className={`shrink-0 text-xs px-2 py-0.5 rounded-md border ${STATUS_COLORS[statusKey] || STATUS_COLORS.offline}`}>
|
|
325
324
|
{statusKey}
|
|
326
325
|
</span>
|
|
327
326
|
</div>
|
|
328
|
-
<div className="text-xs text-gray-500">{s.agent || '--'} · {timeAgo(s.last_seen_at || s.updated_at)}</div>
|
|
329
327
|
{s.task && <div className="truncate text-xs text-gray-500">{s.task}</div>}
|
|
330
328
|
<div className="flex gap-2">
|
|
331
329
|
<button type="button" onClick={() => setChatAlias(s.alias)}
|
package/app/page.tsx
CHANGED
|
@@ -10,6 +10,7 @@ import { AgentCard } from './components/AgentCard';
|
|
|
10
10
|
import { InboxPanel } from './components/InboxPanel';
|
|
11
11
|
import { LoadingSkeleton } from './components/LoadingSkeleton';
|
|
12
12
|
import { NodesEmptyState as EmptyState } from './components/EmptyState';
|
|
13
|
+
import { AliasAvatar } from './components/AliasAvatar';
|
|
13
14
|
import { UserBar } from './components/UserBar';
|
|
14
15
|
import { CommandCenter, useCommandCenter } from './components/CommandCenter';
|
|
15
16
|
import { DispatchPanel } from './components/DispatchPanel';
|
|
@@ -120,7 +121,7 @@ export default function Dashboard() {
|
|
|
120
121
|
</div>
|
|
121
122
|
|
|
122
123
|
{/* Dispatch + User Bar */}
|
|
123
|
-
<div className="flex items-center gap-3 mb-
|
|
124
|
+
<div className="flex items-center gap-3 mb-4">
|
|
124
125
|
<button onClick={() => setShowDispatch(true)}
|
|
125
126
|
className="px-4 py-2 bg-gradient-to-r from-cyan-600 to-blue-600 hover:from-cyan-500 hover:to-blue-500 text-white text-sm font-medium rounded-xl shadow-lg shadow-cyan-500/10 transition-all active:scale-95 shrink-0">
|
|
126
127
|
⚡ Dispatch
|
|
@@ -189,24 +190,97 @@ export default function Dashboard() {
|
|
|
189
190
|
{/* Quick Actions — split into two distinct intents:
|
|
190
191
|
(1) Top: live stat cards (carry data, drill-in on click)
|
|
191
192
|
(2) Bottom: pure nav rail (no number, icon + label)
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
193
|
+
Round 24 — wrap both in a labelled block so the rhythm reads as
|
|
194
|
+
"here are the main jumps" instead of two disconnected strips. */}
|
|
195
|
+
<div className="text-[10px] uppercase tracking-[0.12em] text-gray-600 mb-2">Quick navigation</div>
|
|
195
196
|
<section className="mb-3 grid grid-cols-3 gap-2 sm:gap-3">
|
|
196
|
-
{
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
197
|
+
{(() => {
|
|
198
|
+
// Build breakdown popover content per card. Pure data — pure CSS
|
|
199
|
+
// popover (no JS state) wires the hover-show transition below.
|
|
200
|
+
const idleCount = sessions.filter(s => isOnline(s) && s.status !== 'working').length;
|
|
201
|
+
const offlineCount = total - online;
|
|
202
|
+
const orderedStatuses = ['running', 'replied', 'failed', 'cancelled', 'expired', 'closed', 'created', 'delivered', 'acked'];
|
|
203
|
+
const failedRecent = tasks.filter((t: { status: string }) => t.status === 'failed').length;
|
|
204
|
+
|
|
205
|
+
const cards = [
|
|
206
|
+
{
|
|
207
|
+
href: '/nodes', label: 'Nodes',
|
|
208
|
+
value: `${online}/${total}`,
|
|
209
|
+
sub: `${total > 0 ? Math.round((online / total) * 100) : 0}% online`,
|
|
210
|
+
color: 'text-green-400 border-green-500/20',
|
|
211
|
+
popover: total === 0 ? null : [
|
|
212
|
+
{ k: 'working', v: working, dot: '', color: '#4ade80' },
|
|
213
|
+
{ k: 'idle', v: idleCount, dot: '', color: '#22d3ee' },
|
|
214
|
+
{ k: 'offline', v: offlineCount, dot: '', color: '#9ca3af' },
|
|
215
|
+
],
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
href: '/tasks', label: 'Tasks',
|
|
219
|
+
value: String(Object.values(taskStats).reduce((a, b) => a + b, 0) || 0),
|
|
220
|
+
sub: 'all-time',
|
|
221
|
+
color: 'text-cyan-400 border-cyan-500/20',
|
|
222
|
+
popover: Object.keys(taskStats).length === 0 ? null
|
|
223
|
+
: orderedStatuses
|
|
224
|
+
.filter(s => taskStats[s])
|
|
225
|
+
.map(s => {
|
|
226
|
+
// Inline hex avoids Tailwind purging dynamic
|
|
227
|
+
// `bg-${color}-400` class names.
|
|
228
|
+
const hex = ({
|
|
229
|
+
running: '#4ade80', replied: '#a78bfa', failed: '#f87171',
|
|
230
|
+
cancelled: '#facc15', expired: '#fb923c', closed: '#9ca3af',
|
|
231
|
+
created: '#9ca3af', delivered: '#60a5fa', acked: '#22d3ee',
|
|
232
|
+
} as Record<string, string>)[s] || '#9ca3af';
|
|
233
|
+
return { k: s, v: taskStats[s], dot: '', color: hex };
|
|
234
|
+
}),
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
href: '/tasks?status=failed', label: 'Failed',
|
|
238
|
+
value: String(taskStats['failed'] || 0),
|
|
239
|
+
sub: taskStats['failed'] ? 'needs review' : 'none',
|
|
240
|
+
color: taskStats['failed'] ? 'text-red-400 border-red-500/25' : 'text-gray-500 border-gray-700/30',
|
|
241
|
+
popover: !failedRecent ? [{ k: 'no failures yet', v: '', dot: '', color: '#4b5563' }]
|
|
242
|
+
: [{ k: `${failedRecent} in current view`, v: '', dot: '', color: '#f87171' }],
|
|
243
|
+
},
|
|
244
|
+
];
|
|
245
|
+
|
|
246
|
+
return cards.map(a => (
|
|
247
|
+
<Link
|
|
248
|
+
key={a.href}
|
|
249
|
+
href={a.href}
|
|
250
|
+
prefetch={false}
|
|
251
|
+
className={`anet-stat-link group relative rounded-xl border ${a.color} bg-[#111128] px-3 py-3 transition-all hover:-translate-y-px`}
|
|
252
|
+
>
|
|
253
|
+
<div className="flex items-baseline justify-between">
|
|
254
|
+
<div className={`text-xl font-semibold tabular-nums ${a.color.split(' ')[0]}`}>{a.value}</div>
|
|
255
|
+
<div className="hidden sm:block text-[10px] text-gray-600 group-hover:text-gray-400 transition-colors">View →</div>
|
|
256
|
+
</div>
|
|
257
|
+
<div className="text-[11px] text-gray-400 mt-0.5">{a.label}</div>
|
|
258
|
+
<div className="text-[10px] text-gray-600 mt-px">{a.sub}</div>
|
|
259
|
+
|
|
260
|
+
{/* Hover popover — CSS-only, restrained. Hidden on touch
|
|
261
|
+
(no :hover) so mobile is unaffected. Positioned just
|
|
262
|
+
below the card so it doesn't fight nav rail. */}
|
|
263
|
+
{a.popover && a.popover.length > 0 && (
|
|
264
|
+
<div className="anet-stat-popover hidden md:block pointer-events-none absolute left-2 right-2 top-full mt-1 z-20 rounded-lg border border-[#2a2a4a] bg-[#0d0d1a] shadow-lg shadow-black/30 px-3 py-2 opacity-0 translate-y-[-2px] group-hover:opacity-100 group-hover:translate-y-0 transition-all duration-150 delay-100">
|
|
265
|
+
<div className="text-[10px] text-gray-600 uppercase tracking-wider mb-1.5">Breakdown</div>
|
|
266
|
+
<ul className="space-y-1">
|
|
267
|
+
{a.popover.map(row => (
|
|
268
|
+
<li key={row.k} className="flex items-center gap-2 text-[11px]">
|
|
269
|
+
<span
|
|
270
|
+
className="inline-block w-1.5 h-1.5 rounded-full shrink-0"
|
|
271
|
+
style={{ backgroundColor: row.color }}
|
|
272
|
+
aria-hidden
|
|
273
|
+
/>
|
|
274
|
+
<span className="text-gray-400 capitalize">{row.k}</span>
|
|
275
|
+
{row.v !== '' && <span className="ml-auto text-gray-300 tabular-nums font-medium">{row.v}</span>}
|
|
276
|
+
</li>
|
|
277
|
+
))}
|
|
278
|
+
</ul>
|
|
279
|
+
</div>
|
|
280
|
+
)}
|
|
281
|
+
</Link>
|
|
282
|
+
));
|
|
283
|
+
})()}
|
|
210
284
|
</section>
|
|
211
285
|
|
|
212
286
|
{/* Nav rail — pure navigation, icon + label, no data */}
|
|
@@ -241,9 +315,11 @@ export default function Dashboard() {
|
|
|
241
315
|
<span className={`w-1.5 h-1.5 rounded-full shrink-0 ${
|
|
242
316
|
t.status === 'replied' ? 'bg-purple-400' : t.status === 'running' ? 'bg-green-400' : t.status === 'failed' ? 'bg-red-400' : 'bg-blue-400'
|
|
243
317
|
}`} />
|
|
244
|
-
<
|
|
318
|
+
{t.from_name && <AliasAvatar alias={t.from_name} size={14} />}
|
|
319
|
+
<span className="text-gray-300 shrink-0 max-w-[20%] truncate">{t.from_name || '?'}</span>
|
|
245
320
|
<span className="text-gray-600">→</span>
|
|
246
|
-
<
|
|
321
|
+
{t.to_name && <AliasAvatar alias={t.to_name} size={14} />}
|
|
322
|
+
<span className="text-gray-300 shrink-0 max-w-[20%] truncate">{t.to_name || '?'}</span>
|
|
247
323
|
<span className="text-gray-500 truncate flex-1">{t.content?.slice(0, 40) || '--'}</span>
|
|
248
324
|
<span className={`shrink-0 px-1.5 py-0.5 rounded text-[10px] border ${
|
|
249
325
|
t.status === 'replied' ? 'text-purple-300 border-purple-500/20' : t.status === 'failed' ? 'text-red-300 border-red-500/20' : 'text-gray-500 border-gray-700/30'
|