@sleep2agi/agent-network-dashboard 0.4.5-preview.8 → 0.4.5-preview.80
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/.mcp.json +11 -0
- package/.next/BUILD_ID +1 -1
- package/.next/build-manifest.json +3 -3
- package/.next/diagnostics/route-bundle-stats.json +48 -48
- 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_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +4 -4
- 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 +4 -4
- 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 +4 -4
- 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 +4 -4
- 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 +4 -4
- 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 +4 -4
- 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 +4 -4
- 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_client-reference-manifest.js +1 -1
- package/.next/server/app/server-logs.html +4 -4
- 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 +4 -4
- 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_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/tokens.html +4 -4
- 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 +4 -4
- 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 +4 -4
- 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]__0jl4ukn._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0jl4ukn._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0n46s~k._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0n46s~k._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0w2ay2h._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0w2ay2h._.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/_0nne7mz._.js +3 -0
- package/.next/server/chunks/ssr/_0nne7mz._.js.map +1 -0
- package/.next/server/chunks/ssr/app_0.zbtn5._.js +9 -0
- package/.next/server/chunks/ssr/app_0.zbtn5._.js.map +1 -0
- package/.next/server/chunks/ssr/app_01y-~mp._.js +3 -0
- package/.next/server/chunks/ssr/app_01y-~mp._.js.map +1 -0
- package/.next/server/chunks/ssr/app_04n78pr._.js +3 -0
- package/.next/server/chunks/ssr/app_04n78pr._.js.map +1 -0
- package/.next/server/chunks/ssr/app_0_agk0r._.js +3 -0
- package/.next/server/chunks/ssr/app_0_agk0r._.js.map +1 -0
- package/.next/server/chunks/ssr/app_0aenxef._.js +3 -0
- package/.next/server/chunks/ssr/app_0aenxef._.js.map +1 -0
- package/.next/server/chunks/ssr/app_0aum_g5._.js +3 -0
- package/.next/server/chunks/ssr/app_0aum_g5._.js.map +1 -0
- package/.next/server/chunks/ssr/app_0emvkn2._.js +3 -0
- package/.next/server/chunks/ssr/app_0emvkn2._.js.map +1 -0
- package/.next/server/chunks/ssr/app_0esuljp._.js +3 -0
- package/.next/server/chunks/ssr/app_0esuljp._.js.map +1 -0
- package/.next/server/chunks/ssr/app_0izi28j._.js +1 -1
- package/.next/server/chunks/ssr/app_0izi28j._.js.map +1 -1
- package/.next/server/chunks/ssr/app_components_08ys5y3._.js +5 -5
- package/.next/server/chunks/ssr/app_components_08ys5y3._.js.map +1 -1
- package/.next/server/chunks/ssr/app_server-logs_page_tsx_0_kpp1p._.js +1 -1
- package/.next/server/chunks/ssr/app_server-logs_page_tsx_0_kpp1p._.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 +4 -4
- 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/06uku.~mtk9zz.js +1 -0
- package/.next/static/chunks/07zsecr-~j5xa.js +1 -0
- package/.next/static/chunks/08i3fgrjz9_yj.css +2 -0
- package/.next/static/chunks/0av2~0.96~d~g.js +1 -0
- package/.next/static/chunks/0b64c5k_6b_n8.js +1 -0
- package/.next/static/chunks/{07okhs5561y5y.js → 0c1bp~g7.8ru..js} +1 -1
- package/.next/static/chunks/0dj5~edkdwh1n.js +1 -0
- package/.next/static/chunks/0geyh7e8y2s5f.js +1 -0
- package/.next/static/chunks/0gn0dhq_g343f.js +1 -0
- package/.next/static/chunks/0gtbwcilfu6b7.js +1 -0
- package/.next/static/chunks/0ir50f-dedv4s.js +1 -0
- package/.next/static/chunks/0rk5zmyp19fv8.js +7 -0
- package/.next/static/chunks/0ve696rkee.95.js +7 -0
- package/.next/static/chunks/0why2e264gmro.js +1 -0
- package/.next/static/chunks/0yw01eo779t1c.js +1 -0
- package/.next/static/chunks/11qk8zcquptgr.js +1 -0
- package/.next/static/chunks/158g10ijxldbh.js +1 -0
- package/.next/trace +2 -2
- package/.next/trace-build +1 -1
- package/app/admin/page.tsx +97 -27
- package/app/components/AgentCard.tsx +19 -5
- package/app/components/BroadcastBar.tsx +22 -4
- package/app/components/DispatchPanel.tsx +9 -5
- package/app/components/EmptyState.tsx +77 -4
- package/app/components/HealthBanner.tsx +11 -2
- package/app/components/HelpOverlay.tsx +50 -10
- package/app/components/InboxPanel.tsx +7 -4
- package/app/components/Sidebar.tsx +49 -41
- package/app/components/StatsBar.tsx +59 -36
- package/app/components/TaskChatPanel.tsx +8 -10
- package/app/components/TaskDrawer.tsx +72 -28
- package/app/components/TopoGraph.tsx +183 -31
- package/app/components/utils.ts +13 -0
- package/app/globals.css +90 -0
- package/app/lib/status.ts +85 -0
- package/app/lib/version.ts +7 -0
- package/app/login/page.tsx +40 -7
- package/app/logs/page.tsx +70 -21
- package/app/messages/page.tsx +34 -9
- package/app/node/page.tsx +52 -27
- package/app/nodes/page.tsx +45 -21
- package/app/page.tsx +99 -31
- package/app/server-logs/page.tsx +104 -28
- package/app/settings/networks/page.tsx +40 -16
- package/app/settings/page.tsx +39 -11
- package/app/tasks/[id]/page.tsx +2 -13
- package/app/tasks/page.tsx +66 -73
- package/package.json +1 -1
- package/scripts/fold-only.mjs +47 -0
- package/scripts/loop-review.mjs +270 -0
- package/scripts/topo-shot.mjs +25 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__06w_u7t._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__06w_u7t._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0ehu0jb._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0ehu0jb._.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/_13_v7-3._.js +0 -3
- package/.next/server/chunks/ssr/_13_v7-3._.js.map +0 -1
- package/.next/server/chunks/ssr/app_0.0wpa~._.js +0 -3
- package/.next/server/chunks/ssr/app_0.0wpa~._.js.map +0 -1
- package/.next/server/chunks/ssr/app_0ap_oiq._.js +0 -3
- package/.next/server/chunks/ssr/app_0ap_oiq._.js.map +0 -1
- package/.next/server/chunks/ssr/app_0dk5wao._.js +0 -3
- package/.next/server/chunks/ssr/app_0dk5wao._.js.map +0 -1
- package/.next/server/chunks/ssr/app_0et5yua._.js +0 -3
- package/.next/server/chunks/ssr/app_0et5yua._.js.map +0 -1
- package/.next/server/chunks/ssr/app_10b5blc._.js +0 -9
- package/.next/server/chunks/ssr/app_10b5blc._.js.map +0 -1
- package/.next/server/chunks/ssr/app_11vl967._.js +0 -3
- package/.next/server/chunks/ssr/app_11vl967._.js.map +0 -1
- package/.next/server/chunks/ssr/app_login_page_tsx_0z71ad3._.js +0 -3
- package/.next/server/chunks/ssr/app_login_page_tsx_0z71ad3._.js.map +0 -1
- package/.next/server/chunks/ssr/app_page_tsx_0es_sk2._.js +0 -3
- package/.next/server/chunks/ssr/app_page_tsx_0es_sk2._.js.map +0 -1
- package/.next/static/chunks/02afyxgpty3uf.js +0 -1
- package/.next/static/chunks/02flrewxlf_m_.js +0 -1
- package/.next/static/chunks/051gslfjka1kj.js +0 -1
- package/.next/static/chunks/07oobuohrpigy.js +0 -1
- package/.next/static/chunks/09al3qm76q7nd.js +0 -1
- package/.next/static/chunks/0e7c40p~wq665.js +0 -1
- package/.next/static/chunks/0g7m7zk.v7g6h.js +0 -1
- package/.next/static/chunks/0h.ajsk_uxigl.js +0 -1
- package/.next/static/chunks/0msa~bub38ytf.css +0 -2
- package/.next/static/chunks/0ogeals5ufhho.js +0 -1
- package/.next/static/chunks/0sgvs~8hw4df0.js +0 -1
- package/.next/static/chunks/10ir.g.n~hre0.js +0 -7
- package/.next/static/chunks/10njj7kn9ylkf.js +0 -1
- package/.next/static/chunks/12dcb86elsl-e.js +0 -7
- package/.next/static/chunks/13.dst7liziwo.js +0 -1
- package/.next/static/chunks/132e__998_q9r.js +0 -1
- package/public/file.svg +0 -1
- package/public/globe.svg +0 -1
- package/public/next.svg +0 -1
- package/public/vercel.svg +0 -1
- package/public/window.svg +0 -1
- /package/.next/static/{LQ5MjOWd5QM3a72laNr_D → 5EPRA3x3-w6LIGTySDUkk}/_buildManifest.js +0 -0
- /package/.next/static/{LQ5MjOWd5QM3a72laNr_D → 5EPRA3x3-w6LIGTySDUkk}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{LQ5MjOWd5QM3a72laNr_D → 5EPRA3x3-w6LIGTySDUkk}/_ssgManifest.js +0 -0
|
@@ -31,12 +31,28 @@ interface FlowLink {
|
|
|
31
31
|
const cx = 500;
|
|
32
32
|
const cy = 330;
|
|
33
33
|
const onlineRadius = 220;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
// Round 97 (issue #50): tier into two rings when N > 8; round 98
|
|
35
|
+
// (issue #61): tier into three rings when N > 14. At N=22 (Vincent's
|
|
36
|
+
// network) two rings still leave 11 nodes per ring → inner chord of
|
|
37
|
+
// 88px can't fit a 100px label; three rings give ~⌈N/3⌉ per ring so
|
|
38
|
+
// every tier has enough arc room.
|
|
39
|
+
const onlineTierThreshold = 8;
|
|
40
|
+
const onlineTripleThreshold = 14;
|
|
41
|
+
const onlineInnerRadius = 175;
|
|
42
|
+
const onlineOuterRadius = 260;
|
|
43
|
+
const onlineTripleInnerR = 145;
|
|
44
|
+
const onlineTripleMidR = 215;
|
|
45
|
+
const onlineTripleOuterR = 285;
|
|
46
|
+
const offlineRadius = 325;
|
|
47
|
+
|
|
48
|
+
/** Polar coordinate for a node on a ring. `rotateBy` lets the caller offset
|
|
49
|
+
* the whole ring so two stacked rings don't align radially (round 25: the
|
|
50
|
+
* offline ring gets a half-step rotation so its nodes sit in the gaps
|
|
51
|
+
* between online ones). */
|
|
52
|
+
function polarPoint(index: number, total: number, radius: number, rotateBy = 0) {
|
|
37
53
|
const spread = total <= 2 ? Math.PI : Math.PI * 1.78;
|
|
38
54
|
const start = -Math.PI / 2 - spread / 2;
|
|
39
|
-
const angle = total <= 1 ? -Math.PI / 2 : start + (spread * index) / (total - 1);
|
|
55
|
+
const angle = total <= 1 ? -Math.PI / 2 : start + (spread * index) / (total - 1) + rotateBy;
|
|
40
56
|
return {
|
|
41
57
|
x: cx + radius * Math.cos(angle),
|
|
42
58
|
y: cy + radius * Math.sin(angle),
|
|
@@ -244,12 +260,63 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
244
260
|
const offline = sessions.filter(s => s.status === 'offline' && !sseCount(s));
|
|
245
261
|
const positions: Record<string, Point> = {};
|
|
246
262
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
263
|
+
// Round 97 (issue #50) + 98 (issue #61): three layout modes by N.
|
|
264
|
+
// N ≤ 8 → single ring (r=220)
|
|
265
|
+
// 8 < N ≤ 14 → two rings (inner r=175, outer r=260, half-step rot)
|
|
266
|
+
// N > 14 → three rings (r=145/215/285, ⌈N/3⌉ per ring)
|
|
267
|
+
// Each ring's spread is 1.78π so its node count drives chord length:
|
|
268
|
+
// labels are 100px wide so each ring needs ≥110px chord per node.
|
|
269
|
+
const tripleTier = online.length > onlineTripleThreshold;
|
|
270
|
+
const dualTier = !tripleTier && online.length > onlineTierThreshold;
|
|
271
|
+
let outerOnlineCount = online.length;
|
|
272
|
+
if (tripleTier) {
|
|
273
|
+
const per = Math.ceil(online.length / 3);
|
|
274
|
+
const r1 = online.slice(0, per);
|
|
275
|
+
const r2 = online.slice(per, 2 * per);
|
|
276
|
+
const r3 = online.slice(2 * per);
|
|
277
|
+
r1.forEach((s, index) => {
|
|
278
|
+
positions[s.alias] = polarPoint(index, Math.max(r1.length, 1), onlineTripleInnerR);
|
|
279
|
+
});
|
|
280
|
+
const r1Spread = r1.length <= 2 ? Math.PI : Math.PI * 1.78;
|
|
281
|
+
const r1Step = r1.length > 1 ? r1Spread / (r1.length - 1) : 0;
|
|
282
|
+
r2.forEach((s, index) => {
|
|
283
|
+
positions[s.alias] = polarPoint(index, Math.max(r2.length, 1), onlineTripleMidR, r1Step / 2);
|
|
284
|
+
});
|
|
285
|
+
r3.forEach((s, index) => {
|
|
286
|
+
positions[s.alias] = polarPoint(index, Math.max(r3.length, 1), onlineTripleOuterR);
|
|
287
|
+
});
|
|
288
|
+
outerOnlineCount = r3.length;
|
|
289
|
+
} else if (dualTier) {
|
|
290
|
+
const innerCount = Math.ceil(online.length / 2);
|
|
291
|
+
const outerCount = online.length - innerCount;
|
|
292
|
+
const innerNodes = online.slice(0, innerCount);
|
|
293
|
+
const outerNodes = online.slice(innerCount);
|
|
294
|
+
innerNodes.forEach((s, index) => {
|
|
295
|
+
positions[s.alias] = polarPoint(index, Math.max(innerCount, 1), onlineInnerRadius);
|
|
296
|
+
});
|
|
297
|
+
const innerSpread = innerCount <= 2 ? Math.PI : Math.PI * 1.78;
|
|
298
|
+
const innerStep = innerCount > 1 ? innerSpread / (innerCount - 1) : 0;
|
|
299
|
+
outerNodes.forEach((s, index) => {
|
|
300
|
+
positions[s.alias] = polarPoint(index, Math.max(outerCount, 1), onlineOuterRadius, innerStep / 2);
|
|
301
|
+
});
|
|
302
|
+
outerOnlineCount = outerCount;
|
|
303
|
+
} else {
|
|
304
|
+
online.forEach((s, index) => {
|
|
305
|
+
positions[s.alias] = polarPoint(index, Math.max(online.length, 1), onlineRadius);
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Offset the offline ring radially by half the outermost online step so
|
|
310
|
+
// offline bubbles sit in the angular gaps between online bubbles instead
|
|
311
|
+
// of stacking directly behind them. Also push the outer ring further when
|
|
312
|
+
// there are many offline nodes so labels don't crowd the legend.
|
|
313
|
+
const outerSpreadBase = outerOnlineCount <= 2 ? Math.PI : Math.PI * 1.78;
|
|
314
|
+
const outerStep = outerOnlineCount > 1 ? outerSpreadBase / (outerOnlineCount - 1) : 0;
|
|
315
|
+
const offlineRotation = outerOnlineCount > 0 ? outerStep / 2 : 0;
|
|
316
|
+
const offlineR = offlineRadius + Math.max(0, offline.length - 4) * 6;
|
|
250
317
|
|
|
251
318
|
offline.forEach((s, index) => {
|
|
252
|
-
positions[s.alias] = polarPoint(index, Math.max(offline.length, 1),
|
|
319
|
+
positions[s.alias] = polarPoint(index, Math.max(offline.length, 1), offlineR, offlineRotation);
|
|
253
320
|
});
|
|
254
321
|
|
|
255
322
|
const links = buildFlowLinks(messages, positions);
|
|
@@ -320,15 +387,85 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
320
387
|
<marker id="topo-arrow" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="5" markerHeight="5" orient="auto-start-reverse">
|
|
321
388
|
<path d="M 0 0 L 10 5 L 0 10 z" fill={pal.arrowFill} />
|
|
322
389
|
</marker>
|
|
390
|
+
{/* Round 45: radial radar sweep gradient — bright at center
|
|
391
|
+
(where it meets the hub) fading to leading edge. */}
|
|
392
|
+
<radialGradient id="topo-sweep" cx="0%" cy="50%" r="100%">
|
|
393
|
+
<stop offset="0%" stopColor={isLight ? '#0d9488' : '#22d3ee'} stopOpacity={isLight ? 0.18 : 0.32} />
|
|
394
|
+
<stop offset="70%" stopColor={isLight ? '#0d9488' : '#22d3ee'} stopOpacity={isLight ? 0.10 : 0.18} />
|
|
395
|
+
<stop offset="100%" stopColor={isLight ? '#0d9488' : '#22d3ee'} stopOpacity="0" />
|
|
396
|
+
</radialGradient>
|
|
323
397
|
</defs>
|
|
324
398
|
|
|
325
399
|
<rect width="1000" height="680" fill="url(#topo-panel)" />
|
|
326
400
|
<circle cx={cx} cy={cy} r="330" fill="url(#topo-radar)" />
|
|
327
401
|
|
|
402
|
+
{/* Round 45: subtle star field — 24 deterministic dots scattered
|
|
403
|
+
across the canvas give the radar bg some depth. Skipped on
|
|
404
|
+
light theme so the white surface stays clean. */}
|
|
405
|
+
{!isLight && (
|
|
406
|
+
<g opacity="0.5">
|
|
407
|
+
{Array.from({ length: 28 }).map((_, i) => {
|
|
408
|
+
// Deterministic pseudo-random scatter so positions are
|
|
409
|
+
// stable between renders (no JS hydration mismatch).
|
|
410
|
+
const seed = i * 9301 + 49297;
|
|
411
|
+
const x = ((seed * 13) % 1000);
|
|
412
|
+
const y = ((seed * 7) % 680);
|
|
413
|
+
const r = (i % 3 === 0) ? 1.2 : 0.7;
|
|
414
|
+
return <circle key={i} cx={x} cy={y} r={r} fill="#a5b4fc" opacity={0.35 + (i % 4) * 0.05} />;
|
|
415
|
+
})}
|
|
416
|
+
</g>
|
|
417
|
+
)}
|
|
418
|
+
|
|
419
|
+
{/* Round 45: rotating radar sweep — a 40° wedge with a soft
|
|
420
|
+
leading-edge gradient. Slow 6s rotation reads as a radar
|
|
421
|
+
scan without being noisy. Inline transform-origin on the
|
|
422
|
+
<g> wrapper ensures Chrome / Firefox rotate around (cx,cy)
|
|
423
|
+
instead of the SVG viewBox corner. */}
|
|
424
|
+
<g
|
|
425
|
+
style={{
|
|
426
|
+
transformOrigin: `${cx}px ${cy}px`,
|
|
427
|
+
transformBox: 'view-box',
|
|
428
|
+
}}
|
|
429
|
+
className="anet-topo-sweep"
|
|
430
|
+
opacity={isLight ? 0.7 : 1}
|
|
431
|
+
>
|
|
432
|
+
<path
|
|
433
|
+
d={`M ${cx} ${cy} L ${cx + 330} ${cy} A 330 330 0 0 0 ${cx + 330 * Math.cos(-Math.PI / 4.5)} ${cy + 330 * Math.sin(-Math.PI / 4.5)} Z`}
|
|
434
|
+
fill="url(#topo-sweep)"
|
|
435
|
+
/>
|
|
436
|
+
</g>
|
|
437
|
+
|
|
328
438
|
{/* radar rings */}
|
|
329
439
|
{[90, 170, 250, 330].map(radius => (
|
|
330
440
|
<circle key={radius} cx={cx} cy={cy} r={radius} fill="none" stroke={pal.ringStroke} strokeWidth="1" opacity={isLight ? 0.6 : 0.35} />
|
|
331
441
|
))}
|
|
442
|
+
|
|
443
|
+
{/* Round 50: 4 small particles slowly orbiting the outer ring
|
|
444
|
+
(r=330). Each starts at a different angle (offset 0/0.25/0.5/0.75
|
|
445
|
+
of the cycle) so they're evenly spaced. 16s per revolution is
|
|
446
|
+
slow enough to feel ambient, not noisy. Skipped on light theme
|
|
447
|
+
so the white surface stays clean. */}
|
|
448
|
+
{!isLight && [0, 0.25, 0.5, 0.75].map((phase, i) => (
|
|
449
|
+
<g key={`orbit-${i}`}>
|
|
450
|
+
<circle
|
|
451
|
+
cx={cx + 330} cy={cy}
|
|
452
|
+
r={i === 0 ? 2.8 : 2.2}
|
|
453
|
+
fill="#22d3ee"
|
|
454
|
+
opacity={0.9}
|
|
455
|
+
filter="url(#topo-glow)"
|
|
456
|
+
>
|
|
457
|
+
<animateTransform
|
|
458
|
+
attributeName="transform"
|
|
459
|
+
type="rotate"
|
|
460
|
+
from={`${phase * 360} ${cx} ${cy}`}
|
|
461
|
+
to={`${phase * 360 + 360} ${cx} ${cy}`}
|
|
462
|
+
dur="16s"
|
|
463
|
+
repeatCount="indefinite"
|
|
464
|
+
/>
|
|
465
|
+
</circle>
|
|
466
|
+
</g>
|
|
467
|
+
))}
|
|
468
|
+
|
|
332
469
|
{[0, 30, 60, 90, 120, 150].map(angle => (
|
|
333
470
|
<line
|
|
334
471
|
key={angle}
|
|
@@ -342,21 +479,27 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
342
479
|
/>
|
|
343
480
|
))}
|
|
344
481
|
|
|
345
|
-
{/* hub links
|
|
346
|
-
|
|
482
|
+
{/* hub links — round 46: idle spokes now have animated
|
|
483
|
+
stroke-dashoffset so dashes flow outward from the hub
|
|
484
|
+
("command relay" feel). Active spokes carrying live message
|
|
485
|
+
flow stay as solid bright strokes. */}
|
|
486
|
+
{onlineNodes.map((session, idx) => {
|
|
347
487
|
const pos = nodePositions[session.alias];
|
|
348
488
|
if (!pos) return null;
|
|
349
489
|
const path = curvePath({ x: cx, y: cy }, pos, 0);
|
|
490
|
+
const isActiveSpoke = activeAliases.has(session.alias);
|
|
350
491
|
|
|
351
492
|
return (
|
|
352
493
|
<path
|
|
353
494
|
key={`hub-${session.alias}`}
|
|
354
495
|
d={path}
|
|
355
496
|
fill="none"
|
|
356
|
-
stroke={
|
|
357
|
-
strokeWidth={
|
|
358
|
-
strokeDasharray={
|
|
359
|
-
opacity={
|
|
497
|
+
stroke={isActiveSpoke ? pal.spokeStroke.active : pal.spokeStroke.idle}
|
|
498
|
+
strokeWidth={isActiveSpoke ? 2 : 1}
|
|
499
|
+
strokeDasharray={isActiveSpoke ? 'none' : '6 14'}
|
|
500
|
+
opacity={isActiveSpoke ? 0.7 : 0.45}
|
|
501
|
+
className={isActiveSpoke ? undefined : 'anet-topo-spoke-flow'}
|
|
502
|
+
style={isActiveSpoke ? undefined : { animationDelay: `${-(idx * 0.25)}s` }}
|
|
360
503
|
/>
|
|
361
504
|
);
|
|
362
505
|
})}
|
|
@@ -399,27 +542,32 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
399
542
|
);
|
|
400
543
|
})}
|
|
401
544
|
|
|
402
|
-
{/* center hub —
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
logo or label. Closes agent-network#5. */}
|
|
545
|
+
{/* center hub — round 39: enlarged from r=6 (12px) to r=10 (20px)
|
|
546
|
+
core so the "control plane" reads as the network's anchor,
|
|
547
|
+
not a stray particle. Static halo ring at r=18 grounds it
|
|
548
|
+
visually; two outward pulses keep the "signal source" idea
|
|
549
|
+
from r17. Light theme gets a paler core to avoid hot-spotting
|
|
550
|
+
the bg. */}
|
|
409
551
|
<g>
|
|
552
|
+
{/* static grounding halo — sits underneath, low opacity */}
|
|
553
|
+
<circle
|
|
554
|
+
cx={cx} cy={cy} r="18"
|
|
555
|
+
fill={isLight ? '#d1fae5' : '#10b981'}
|
|
556
|
+
opacity={isLight ? 0.35 : 0.10}
|
|
557
|
+
/>
|
|
410
558
|
{/* outer pulse 1 */}
|
|
411
|
-
<circle cx={cx} cy={cy} r="
|
|
412
|
-
<animate attributeName="r" values="
|
|
559
|
+
<circle cx={cx} cy={cy} r="10" fill="none" stroke="#10b981" strokeWidth="1.5">
|
|
560
|
+
<animate attributeName="r" values="10;38;10" dur="2.4s" repeatCount="indefinite" />
|
|
413
561
|
<animate attributeName="opacity" values="0.55;0;0.55" dur="2.4s" repeatCount="indefinite" />
|
|
414
562
|
</circle>
|
|
415
563
|
{/* outer pulse 2 (delayed 1.2s) */}
|
|
416
|
-
<circle cx={cx} cy={cy} r="
|
|
417
|
-
<animate attributeName="r" values="
|
|
564
|
+
<circle cx={cx} cy={cy} r="10" fill="none" stroke="#10b981" strokeWidth="1.5" opacity="0">
|
|
565
|
+
<animate attributeName="r" values="10;38;10" dur="2.4s" begin="1.2s" repeatCount="indefinite" />
|
|
418
566
|
<animate attributeName="opacity" values="0.45;0;0.45" dur="2.4s" begin="1.2s" repeatCount="indefinite" />
|
|
419
567
|
</circle>
|
|
420
|
-
{/* core
|
|
421
|
-
<circle cx={cx} cy={cy} r="
|
|
422
|
-
<circle cx={cx} cy={cy} r="
|
|
568
|
+
{/* core — 20px diameter, larger inner highlight reads as a "lit lamp" */}
|
|
569
|
+
<circle cx={cx} cy={cy} r="10" fill={isLight ? '#059669' : '#10b981'} />
|
|
570
|
+
<circle cx={cx} cy={cy} r="5" fill="#d1fae5" opacity="0.9" />
|
|
423
571
|
</g>
|
|
424
572
|
|
|
425
573
|
{/* agent nodes */}
|
|
@@ -441,7 +589,7 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
441
589
|
<animate attributeName="opacity" values={isLight ? '0.12;0.02;0.12' : '0.18;0.04;0.18'} dur="2.4s" repeatCount="indefinite" />
|
|
442
590
|
</circle>
|
|
443
591
|
)}
|
|
444
|
-
<circle cx={pos.x} cy={pos.y} r={radius + 8} fill={status.halo} opacity={isOnline ? (isLight ? 0.85 : 0.
|
|
592
|
+
<circle cx={pos.x} cy={pos.y} r={radius + 8} fill={status.halo} opacity={isOnline ? (isLight ? 0.85 : 0.65) : (isLight ? 0.4 : 0.25)} />
|
|
445
593
|
<circle
|
|
446
594
|
cx={pos.x}
|
|
447
595
|
cy={pos.y}
|
|
@@ -459,10 +607,14 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
459
607
|
</path>
|
|
460
608
|
)}
|
|
461
609
|
|
|
610
|
+
{/* Round 98 (issue #61): label rect 124px → 100px so a
|
|
611
|
+
3-ring layout (inner r=145, 8 nodes) still has room for
|
|
612
|
+
the label without overlapping a neighbour. Truncate
|
|
613
|
+
tightened from 14 → 12 chars to match. */}
|
|
462
614
|
<g transform={`translate(${pos.x}, ${pos.y + radius + 22})`}>
|
|
463
|
-
<rect x="-
|
|
615
|
+
<rect x="-50" y="-14" width="100" height="42" rx="6" fill={pal.labelBox.fill} stroke={pal.labelBox.stroke} opacity={isLight ? 1 : 0.94} />
|
|
464
616
|
<text x="0" y="1" textAnchor="middle" fill={status.text} fontSize="12" fontFamily="monospace" fontWeight="700">
|
|
465
|
-
{truncate(session.alias,
|
|
617
|
+
{truncate(session.alias, 12)}
|
|
466
618
|
</text>
|
|
467
619
|
<text x="0" y="17" textAnchor="middle" fill={status.primary} fontSize="9" fontFamily="monospace">
|
|
468
620
|
{status.label}{isOnline && sseCountFor != null ? ` sse:${sseCountFor}` : ''}
|
package/app/components/utils.ts
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
/** Round 82: strip the most common markdown markup from agent-to-agent
|
|
2
|
+
* task / message bodies so single-line table previews read cleanly.
|
|
3
|
+
* Full original content stays in the expanded row / drawer / title=. */
|
|
4
|
+
export function previewContent(raw: string | null | undefined): string {
|
|
5
|
+
if (!raw) return '--';
|
|
6
|
+
return raw
|
|
7
|
+
.replace(/!\[([^\]]*)\]\([^)]*\)/g, '$1') // images:  → alt
|
|
8
|
+
.replace(/\[([^\]]+)\]\([^)]*\)/g, '$1') // links: [text](href) → text
|
|
9
|
+
.replace(/`([^`]+)`/g, '$1') // inline code: `x` → x
|
|
10
|
+
.replace(/\s+/g, ' ') // collapse whitespace
|
|
11
|
+
.trim() || '--';
|
|
12
|
+
}
|
|
13
|
+
|
|
1
14
|
export function timeAgo(dateStr: string): string {
|
|
2
15
|
if (!dateStr) return '--';
|
|
3
16
|
const diff = Date.now() - new Date(dateStr.replace(' ', 'T') + 'Z').getTime();
|
package/app/globals.css
CHANGED
|
@@ -811,3 +811,93 @@ body {
|
|
|
811
811
|
color: #92400e !important;
|
|
812
812
|
border-color: rgba(217, 119, 6, 0.28) !important;
|
|
813
813
|
}
|
|
814
|
+
|
|
815
|
+
/* Round 30 — global drawer entrance keyframes so TaskDrawer, TaskChatPanel,
|
|
816
|
+
and any future right-side panel share one source. Previously the
|
|
817
|
+
keyframe lived inside TaskChatPanel's <style jsx global>; if that
|
|
818
|
+
component never mounted, the keyframe was missing for TaskDrawer. */
|
|
819
|
+
@keyframes anet-slide-in {
|
|
820
|
+
from { transform: translateX(100%); }
|
|
821
|
+
to { transform: translateX(0); }
|
|
822
|
+
}
|
|
823
|
+
.animate-slide-in { animation: anet-slide-in 0.2s ease-out; }
|
|
824
|
+
|
|
825
|
+
@keyframes anet-fade-in {
|
|
826
|
+
from { opacity: 0; }
|
|
827
|
+
to { opacity: 1; }
|
|
828
|
+
}
|
|
829
|
+
.anet-fade-in { animation: anet-fade-in 0.15s ease-out; }
|
|
830
|
+
|
|
831
|
+
/* Round 36 — current-step ring pulse on TaskDrawer timeline. Halo gently
|
|
832
|
+
breathes around the active step's dot so users can spot "task is here". */
|
|
833
|
+
@keyframes anet-current-step-pulse-kf {
|
|
834
|
+
0%, 100% { transform: scale(1); opacity: 0.6; }
|
|
835
|
+
50% { transform: scale(1.25); opacity: 0.0; }
|
|
836
|
+
}
|
|
837
|
+
.anet-current-step-pulse {
|
|
838
|
+
animation: anet-current-step-pulse-kf 1.6s ease-in-out infinite;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
/* Round 45 — TopoGraph rotating radar sweep wedge. 6s slow rotation
|
|
842
|
+
gives the topology canvas a real "radar scan" feel without distracting
|
|
843
|
+
from the agent nodes. */
|
|
844
|
+
@keyframes anet-topo-sweep-kf {
|
|
845
|
+
0% { transform: rotate(0deg); }
|
|
846
|
+
100% { transform: rotate(360deg); }
|
|
847
|
+
}
|
|
848
|
+
.anet-topo-sweep {
|
|
849
|
+
animation: anet-topo-sweep-kf 6s linear infinite;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
/* Round 46 — animated dashed spokes flowing from hub outward. Each spoke
|
|
853
|
+
gets a small animationDelay (set inline) so the dashes don't all flow
|
|
854
|
+
in sync — reads as "command relay" pulse rather than a metronome. */
|
|
855
|
+
@keyframes anet-topo-spoke-flow-kf {
|
|
856
|
+
from { stroke-dashoffset: 0; }
|
|
857
|
+
to { stroke-dashoffset: -40; }
|
|
858
|
+
}
|
|
859
|
+
.anet-topo-spoke-flow {
|
|
860
|
+
animation: anet-topo-spoke-flow-kf 2.4s linear infinite;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
/* ─────────────────────────────────────────────────────────────────────
|
|
864
|
+
Round 29 — prefers-reduced-motion a11y sweep
|
|
865
|
+
|
|
866
|
+
Rounds 19~27 accumulated a lot of small animations: Tasks row 0fr↔1fr
|
|
867
|
+
expand, chevron rotate, hover bg shifts, Cmd+K popover, TaskChatPanel
|
|
868
|
+
slide-in, brand/skeleton pulses already had their own guards.
|
|
869
|
+
|
|
870
|
+
For users who set system "reduce motion": neutralise everything in one
|
|
871
|
+
pass. Don't kill state changes outright — only their durations and
|
|
872
|
+
keyframes. Hover and focus colours still apply, just instantly.
|
|
873
|
+
───────────────────────────────────────────────────────────────────── */
|
|
874
|
+
@media (prefers-reduced-motion: reduce) {
|
|
875
|
+
*,
|
|
876
|
+
*::before,
|
|
877
|
+
*::after {
|
|
878
|
+
animation-duration: 0.001ms !important;
|
|
879
|
+
animation-iteration-count: 1 !important;
|
|
880
|
+
transition-duration: 0.001ms !important;
|
|
881
|
+
scroll-behavior: auto !important;
|
|
882
|
+
}
|
|
883
|
+
/* Skeleton pulse already has its own guard; harden in case the bar
|
|
884
|
+
class is reused without anet-skeleton-pulse. */
|
|
885
|
+
.anet-skeleton-bar {
|
|
886
|
+
animation: none !important;
|
|
887
|
+
}
|
|
888
|
+
/* Brand pulse hidden — green/red dot still indicates state via color. */
|
|
889
|
+
.anet-brand-pulse {
|
|
890
|
+
animation: none !important;
|
|
891
|
+
}
|
|
892
|
+
/* Drawer entrance kills — keyframes promoted to globals in r30. */
|
|
893
|
+
.animate-slide-in,
|
|
894
|
+
.anet-fade-in,
|
|
895
|
+
.anet-current-step-pulse,
|
|
896
|
+
.anet-topo-sweep,
|
|
897
|
+
.anet-topo-spoke-flow {
|
|
898
|
+
animation: none !important;
|
|
899
|
+
}
|
|
900
|
+
/* Tasks row grid 0fr↔1fr keep working (it's the toggle mechanism),
|
|
901
|
+
but the smooth transition just snaps. Already handled via the
|
|
902
|
+
blanket transition-duration override above. */
|
|
903
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/** Single source of truth for task status colors. Both `/tasks` (chip
|
|
2
|
+
* strip, badge, distribution bar) and the Overview Recent Activity row
|
|
3
|
+
* used to ship their own incomplete maps; round 66 consolidates them
|
|
4
|
+
* here so adding a new status updates every consumer at once.
|
|
5
|
+
*
|
|
6
|
+
* Order in `TASK_STATUSES` is chronological-ish: lifecycle progress on
|
|
7
|
+
* the left, terminal-good (`closed`) in the middle, terminal-bad
|
|
8
|
+
* (`failed`/`cancelled`/`expired`) on the right. */
|
|
9
|
+
|
|
10
|
+
export const TASK_STATUSES = [
|
|
11
|
+
'created',
|
|
12
|
+
'delivered',
|
|
13
|
+
'acked',
|
|
14
|
+
'running',
|
|
15
|
+
'replied',
|
|
16
|
+
'closed',
|
|
17
|
+
'failed',
|
|
18
|
+
'cancelled',
|
|
19
|
+
'expired',
|
|
20
|
+
] as const;
|
|
21
|
+
|
|
22
|
+
export type TaskStatus = typeof TASK_STATUSES[number];
|
|
23
|
+
|
|
24
|
+
/** Pill / chip background+text+border. Tailwind classes, not inlined hex,
|
|
25
|
+
* because chips are static class names safe from purge. */
|
|
26
|
+
export const STATUS_CHIP_CLASS: Record<string, string> = {
|
|
27
|
+
created: 'bg-gray-500/10 text-gray-400 border-gray-500/20',
|
|
28
|
+
delivered: 'bg-blue-500/10 text-blue-300 border-blue-500/20',
|
|
29
|
+
acked: 'bg-cyan-500/10 text-cyan-300 border-cyan-500/20',
|
|
30
|
+
running: 'bg-green-500/10 text-green-300 border-green-500/20',
|
|
31
|
+
replied: 'bg-purple-500/10 text-purple-300 border-purple-500/20',
|
|
32
|
+
closed: 'bg-gray-500/10 text-gray-500 border-gray-500/20',
|
|
33
|
+
failed: 'bg-red-500/10 text-red-300 border-red-500/20',
|
|
34
|
+
cancelled: 'bg-yellow-500/10 text-yellow-300 border-yellow-500/20',
|
|
35
|
+
expired: 'bg-orange-500/10 text-orange-300 border-orange-500/20',
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/** Inline hex dots — used wherever Tailwind would purge dynamic class
|
|
39
|
+
* names (`bg-${family}-400`). Style attribute carries the color. */
|
|
40
|
+
export const STATUS_DOT_HEX: Record<string, string> = {
|
|
41
|
+
created: '#9ca3af',
|
|
42
|
+
delivered: '#60a5fa',
|
|
43
|
+
acked: '#22d3ee',
|
|
44
|
+
running: '#4ade80',
|
|
45
|
+
replied: '#a78bfa',
|
|
46
|
+
closed: '#6b7280',
|
|
47
|
+
failed: '#f87171',
|
|
48
|
+
cancelled: '#facc15',
|
|
49
|
+
expired: '#fb923c',
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/** Session lifecycle (distinct from task lifecycle above). Shared by
|
|
53
|
+
* /nodes status pills and /admin Online Sessions row chips so an
|
|
54
|
+
* agent in `blocked` state reads the same color everywhere.
|
|
55
|
+
* Round 91 — extracted from app/nodes/page.tsx. */
|
|
56
|
+
export const SESSION_STATUS_CHIP_CLASS: Record<string, string> = {
|
|
57
|
+
working: 'bg-green-500/10 text-green-300 border-green-500/20',
|
|
58
|
+
idle: 'bg-blue-500/10 text-blue-300 border-blue-500/20',
|
|
59
|
+
blocked: 'bg-yellow-500/10 text-yellow-300 border-yellow-500/20',
|
|
60
|
+
error: 'bg-red-500/10 text-red-300 border-red-500/20',
|
|
61
|
+
offline: 'bg-gray-500/10 text-gray-500 border-gray-500/20',
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/** Text-only color for session status. Used where a chip background
|
|
65
|
+
* would be too heavy — e.g. /node detail header status label. */
|
|
66
|
+
export const SESSION_STATUS_TEXT_CLASS: Record<string, string> = {
|
|
67
|
+
working: 'text-green-400',
|
|
68
|
+
idle: 'text-blue-400',
|
|
69
|
+
blocked: 'text-yellow-400',
|
|
70
|
+
error: 'text-red-400',
|
|
71
|
+
offline: 'text-gray-500',
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/** Solid bar segment background for the Tasks distribution bar. */
|
|
75
|
+
export const STATUS_BAR_CLASS: Record<string, string> = {
|
|
76
|
+
created: 'bg-gray-500',
|
|
77
|
+
delivered: 'bg-blue-500',
|
|
78
|
+
acked: 'bg-cyan-500',
|
|
79
|
+
running: 'bg-green-500',
|
|
80
|
+
replied: 'bg-purple-500',
|
|
81
|
+
closed: 'bg-gray-600',
|
|
82
|
+
failed: 'bg-red-500',
|
|
83
|
+
cancelled: 'bg-yellow-500',
|
|
84
|
+
expired: 'bg-orange-500',
|
|
85
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Dashboard package version sourced from package.json at build time.
|
|
2
|
+
* Importing JSON requires `resolveJsonModule: true` in tsconfig, which
|
|
3
|
+
* Next sets by default. Bumping is automated by the publish flow, so this
|
|
4
|
+
* surfaces the real preview number in Settings → Dashboard. */
|
|
5
|
+
import pkg from '../../package.json' assert { type: 'json' };
|
|
6
|
+
|
|
7
|
+
export const DASHBOARD_VERSION: string = pkg.version;
|
package/app/login/page.tsx
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { FormEvent, useState } from 'react';
|
|
4
|
+
import { DASHBOARD_VERSION } from '../lib/version';
|
|
4
5
|
|
|
5
6
|
export default function LoginPage() {
|
|
6
7
|
const [mode, setMode] = useState<'login' | 'register'>('login');
|
|
7
8
|
const [username, setUsername] = useState('');
|
|
8
9
|
const [password, setPassword] = useState('');
|
|
10
|
+
const [showPassword, setShowPassword] = useState(false);
|
|
9
11
|
const [error, setError] = useState('');
|
|
10
12
|
const [pending, setPending] = useState(false);
|
|
11
13
|
|
|
@@ -68,7 +70,9 @@ export default function LoginPage() {
|
|
|
68
70
|
</div>
|
|
69
71
|
<h1 className="text-[22px] sm:text-2xl font-semibold text-white tracking-tight">Agent Network</h1>
|
|
70
72
|
<p className="text-[13px] text-gray-500 mt-1.5 leading-snug">
|
|
71
|
-
|
|
73
|
+
{mode === 'login'
|
|
74
|
+
? <>Welcome back · <span className="text-gray-600">Tasks · Mesh · Messages</span></>
|
|
75
|
+
: <>Create your account · <span className="text-gray-600">Free preview · no credit card</span></>}
|
|
72
76
|
</p>
|
|
73
77
|
</div>
|
|
74
78
|
|
|
@@ -93,22 +97,51 @@ export default function LoginPage() {
|
|
|
93
97
|
<label htmlFor="password" className="block text-xs text-gray-500 mb-2 uppercase tracking-wider">
|
|
94
98
|
Password
|
|
95
99
|
</label>
|
|
96
|
-
<
|
|
97
|
-
|
|
98
|
-
|
|
100
|
+
<div className="relative">
|
|
101
|
+
<input id="password" type={showPassword ? 'text' : 'password'} value={password} onChange={e => setPassword(e.target.value)}
|
|
102
|
+
placeholder="Enter password"
|
|
103
|
+
className="w-full bg-[#0a0a15] border border-[#2a2a4a] rounded-lg px-4 pr-11 py-3 text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:ring-1 focus:ring-cyan-500/20 focus:outline-none transition-all" />
|
|
104
|
+
<button
|
|
105
|
+
type="button"
|
|
106
|
+
onClick={() => setShowPassword(s => !s)}
|
|
107
|
+
tabIndex={-1}
|
|
108
|
+
aria-label={showPassword ? 'Hide password' : 'Show password'}
|
|
109
|
+
className="absolute inset-y-0 right-0 flex items-center px-3 text-gray-600 hover:text-gray-300 transition-colors"
|
|
110
|
+
>
|
|
111
|
+
{showPassword ? (
|
|
112
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
|
|
113
|
+
<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24" />
|
|
114
|
+
<line x1="1" y1="1" x2="23" y2="23" />
|
|
115
|
+
</svg>
|
|
116
|
+
) : (
|
|
117
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
|
|
118
|
+
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" />
|
|
119
|
+
<circle cx="12" cy="12" r="3" />
|
|
120
|
+
</svg>
|
|
121
|
+
)}
|
|
122
|
+
</button>
|
|
123
|
+
</div>
|
|
99
124
|
|
|
100
125
|
{error && (
|
|
101
126
|
<div className="mt-3 text-sm text-red-300 bg-red-500/5 border border-red-500/10 rounded-lg px-3 py-2">{error}</div>
|
|
102
127
|
)}
|
|
103
128
|
|
|
104
|
-
<button type="submit" disabled={pending || !password}
|
|
105
|
-
className="mt-5 w-full px-4 py-3 bg-gradient-to-r from-cyan-600 to-blue-600 hover:from-cyan-500 hover:to-blue-500 disabled:from-gray-800 disabled:to-gray-800 disabled:text-gray-600 text-white text-sm font-medium rounded-lg transition-all shadow-lg shadow-cyan-500/10 hover:shadow-cyan-500/20 disabled:shadow-none">
|
|
106
|
-
{pending
|
|
129
|
+
<button type="submit" disabled={pending || !password || !username.trim()}
|
|
130
|
+
className="mt-5 w-full flex items-center justify-center gap-2 px-4 py-3 bg-gradient-to-r from-cyan-600 to-blue-600 hover:from-cyan-500 hover:to-blue-500 disabled:from-gray-800 disabled:to-gray-800 disabled:text-gray-600 text-white text-sm font-medium rounded-lg transition-all shadow-lg shadow-cyan-500/10 hover:shadow-cyan-500/20 disabled:shadow-none">
|
|
131
|
+
{pending && (
|
|
132
|
+
<svg aria-hidden className="w-4 h-4 animate-spin" viewBox="0 0 24 24" fill="none">
|
|
133
|
+
<circle cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="3" opacity="0.25" />
|
|
134
|
+
<path d="M4 12a8 8 0 018-8" stroke="currentColor" strokeWidth="3" strokeLinecap="round" />
|
|
135
|
+
</svg>
|
|
136
|
+
)}
|
|
137
|
+
<span>{pending ? (mode === 'login' ? 'Signing in…' : 'Registering…') : (mode === 'login' ? 'Sign in' : 'Create account')}</span>
|
|
107
138
|
</button>
|
|
108
139
|
</form>
|
|
109
140
|
|
|
110
141
|
<p className="text-center text-[11px] text-gray-700 mt-6">
|
|
111
142
|
Sleep2AGI · Apache-2.0 · <a href="https://anet.sh" className="hover:text-gray-500 transition-colors">anet.sh</a>
|
|
143
|
+
<span className="mx-1 text-gray-800">·</span>
|
|
144
|
+
<span className="font-mono" title="@sleep2agi/agent-network-dashboard">v{DASHBOARD_VERSION}</span>
|
|
112
145
|
</p>
|
|
113
146
|
</div>
|
|
114
147
|
</main>
|