@sleep2agi/agent-network-dashboard 0.4.5-preview.7 → 0.4.5-preview.71
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 +49 -49
- 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]__02ee4km._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__02ee4km._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0n4l2is._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0n4l2is._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0o8yj3c._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0o8yj3c._.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_0aum_g5._.js +3 -0
- package/.next/server/chunks/ssr/app_0aum_g5._.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_0py1sns._.js +3 -0
- package/.next/server/chunks/ssr/app_0py1sns._.js.map +1 -0
- package/.next/server/chunks/ssr/app_0tncr1_._.js +3 -0
- package/.next/server/chunks/ssr/app_0tncr1_._.js.map +1 -0
- 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/013_kw~b7_tqy.js +1 -0
- package/.next/static/chunks/04dbz0h45njew.js +1 -0
- package/.next/static/chunks/06uku.~mtk9zz.js +1 -0
- package/.next/static/chunks/07gtk5bhf8gn9.js +1 -0
- package/.next/static/chunks/08scyy1.s_5my.js +1 -0
- package/.next/static/chunks/09oxaqu0qn3c9.js +1 -0
- package/.next/static/chunks/0b8qzoyn-7e~f.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/0glm3gl882al6.js +1 -0
- package/.next/static/chunks/0gnf.w4c8z.l~.js +1 -0
- package/.next/static/chunks/0iwn.7hiigyrz.js +7 -0
- package/.next/static/chunks/0l8112gmzl31s.js +1 -0
- package/.next/static/chunks/0rk5zmyp19fv8.js +7 -0
- package/.next/static/chunks/0uh.dd0_.y_dh.js +1 -0
- package/.next/static/chunks/0vn.u7tcybvik.js +1 -0
- package/.next/static/chunks/13u~uqoww_0ib.css +2 -0
- package/.next/trace +2 -2
- package/.next/trace-build +1 -1
- package/app/admin/page.tsx +90 -26
- 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 +118 -24
- package/app/components/utils.ts +13 -0
- package/app/globals.css +90 -0
- package/app/lib/status.ts +63 -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 +24 -7
- package/app/node/page.tsx +48 -25
- package/app/nodes/page.tsx +37 -9
- package/app/page.tsx +107 -36
- package/app/server-logs/page.tsx +97 -25
- package/app/settings/networks/page.tsx +37 -13
- 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 +44 -0
- package/scripts/loop-review.mjs +226 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__06pu~4t._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__06pu~4t._.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/_0uw5cjt._.js +0 -3
- package/.next/server/chunks/ssr/_0uw5cjt._.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_0k8xbx~._.js +0 -9
- package/.next/server/chunks/ssr/app_0k8xbx~._.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/02flrewxlf_m_.js +0 -1
- package/.next/static/chunks/051gslfjka1kj.js +0 -1
- package/.next/static/chunks/06t_vzei8s9_h.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/0ogeals5ufhho.js +0 -1
- package/.next/static/chunks/0v~9m62eciynd.js +0 -1
- package/.next/static/chunks/0xh3wr5rwvifr.css +0 -2
- package/.next/static/chunks/0z.pzy8zpa~js.js +0 -7
- package/.next/static/chunks/1031v6nw33ud3.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/11v5tec4s1168.js +0 -1
- package/.next/static/chunks/13.dst7liziwo.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/{kyIvxswGu-u-KGQRAaXfV → C7A8u316NGa1ZA9zcEOFm}/_buildManifest.js +0 -0
- /package/.next/static/{kyIvxswGu-u-KGQRAaXfV → C7A8u316NGa1ZA9zcEOFm}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{kyIvxswGu-u-KGQRAaXfV → C7A8u316NGa1ZA9zcEOFm}/_ssgManifest.js +0 -0
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useState, useEffect, useRef, useCallback } from 'react';
|
|
4
4
|
import { timeAgo } from './utils';
|
|
5
|
+
import { AliasAvatar } from './AliasAvatar';
|
|
5
6
|
|
|
6
7
|
interface ChatTask {
|
|
7
8
|
task_id: string;
|
|
@@ -386,7 +387,10 @@ export function TaskChatPanel({ alias, onClose, inline, availableNodes }: TaskCh
|
|
|
386
387
|
{m.result && (
|
|
387
388
|
<div className="flex justify-start">
|
|
388
389
|
<div className="max-w-[85%] bg-green-500/8 border border-green-500/15 rounded-2xl rounded-bl-md px-4 py-2.5 shadow-sm">
|
|
389
|
-
<div className="
|
|
390
|
+
<div className="flex items-center gap-1.5 mb-1.5">
|
|
391
|
+
{m.to_name && <AliasAvatar alias={m.to_name} size={14} />}
|
|
392
|
+
<span className="text-[10px] text-gray-200 font-medium">{m.to_name}</span>
|
|
393
|
+
</div>
|
|
390
394
|
<div className="text-[13px] text-gray-200">
|
|
391
395
|
<MarkdownContent text={m.result} />
|
|
392
396
|
</div>
|
|
@@ -422,7 +426,7 @@ export function TaskChatPanel({ alias, onClose, inline, availableNodes }: TaskCh
|
|
|
422
426
|
{filteredMentions.map(node => (
|
|
423
427
|
<button key={node} onClick={() => insertMention(node)}
|
|
424
428
|
className="w-full flex items-center gap-2 px-3 py-2 text-xs text-left hover:bg-cyan-500/10 transition-colors">
|
|
425
|
-
<
|
|
429
|
+
<AliasAvatar alias={node} size={16} />
|
|
426
430
|
<span className="text-gray-300">{node}</span>
|
|
427
431
|
</button>
|
|
428
432
|
))}
|
|
@@ -471,14 +475,12 @@ export function TaskChatPanel({ alias, onClose, inline, availableNodes }: TaskCh
|
|
|
471
475
|
|
|
472
476
|
return (
|
|
473
477
|
<>
|
|
474
|
-
<div className="fixed inset-0 bg-black/30 z-40 lg:hidden" onClick={onClose} />
|
|
478
|
+
<div className="fixed inset-0 bg-black/30 z-40 lg:hidden anet-fade-in" onClick={onClose} />
|
|
475
479
|
<div className="fixed top-0 right-0 h-full w-full lg:w-[500px] bg-[#0a0a1a] border-l border-[#2a2a4a] z-50 flex flex-col shadow-2xl shadow-black/60 animate-slide-in">
|
|
476
480
|
{/* Header */}
|
|
477
481
|
<div className="flex items-center justify-between px-4 py-3 border-b border-[#2a2a4a] bg-[#0d0d1a]">
|
|
478
482
|
<div className="flex items-center gap-3">
|
|
479
|
-
<
|
|
480
|
-
{alias.slice(0, 2)}
|
|
481
|
-
</div>
|
|
483
|
+
<AliasAvatar alias={alias} size={32} />
|
|
482
484
|
<div>
|
|
483
485
|
<div className="text-sm font-semibold text-white">{alias}</div>
|
|
484
486
|
<div className="text-[10px] text-gray-500">{pollingIds.size > 0 ? 'Processing...' : 'Ready'}</div>
|
|
@@ -492,10 +494,6 @@ export function TaskChatPanel({ alias, onClose, inline, availableNodes }: TaskCh
|
|
|
492
494
|
</div>
|
|
493
495
|
{chatContent}
|
|
494
496
|
</div>
|
|
495
|
-
<style jsx global>{`
|
|
496
|
-
@keyframes slide-in { from { transform: translateX(100%); } to { transform: translateX(0); } }
|
|
497
|
-
.animate-slide-in { animation: slide-in 0.2s ease-out; }
|
|
498
|
-
`}</style>
|
|
499
497
|
</>
|
|
500
498
|
);
|
|
501
499
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { useEffect, useState } from 'react';
|
|
4
4
|
import { timeAgo } from './utils';
|
|
5
5
|
import Link from 'next/link';
|
|
6
|
+
import { AliasAvatar } from './AliasAvatar';
|
|
6
7
|
|
|
7
8
|
interface TaskDetail {
|
|
8
9
|
task_id: string;
|
|
@@ -58,12 +59,20 @@ export function TaskDrawer({ taskId, onClose }: TaskDrawerProps) {
|
|
|
58
59
|
})();
|
|
59
60
|
}, [taskId]);
|
|
60
61
|
|
|
62
|
+
/** Timeline steps — same 4 hops every task goes through. `time` is set
|
|
63
|
+
* when the hop has happened; missing time means pending. Round 36 adds
|
|
64
|
+
* `key` for the "current step" highlight + the `done` flag for layout. */
|
|
61
65
|
const timeline = task ? [
|
|
62
|
-
{ label: 'Created',
|
|
63
|
-
{ label: 'Delivered', time: task.delivered_at,
|
|
64
|
-
{ label: 'Started',
|
|
65
|
-
{ label: 'Completed', time: task.completed_at,
|
|
66
|
-
] : [];
|
|
66
|
+
{ key: 'created', label: 'Created', time: task.created_at, color: 'bg-gray-400' },
|
|
67
|
+
{ key: 'delivered', label: 'Delivered', time: task.delivered_at, color: 'bg-blue-400' },
|
|
68
|
+
{ key: 'started', label: 'Started', time: task.started_at, color: 'bg-green-400' },
|
|
69
|
+
{ key: 'completed', label: 'Completed', time: task.completed_at, color: 'bg-purple-400'},
|
|
70
|
+
].map(s => ({ ...s, done: !!s.time })) : [];
|
|
71
|
+
|
|
72
|
+
/** Index of the highest-completed step. The step *after* this index is
|
|
73
|
+
* the "current" one (in-progress). When all are done, currentIdx = -1. */
|
|
74
|
+
const currentStepIdx = timeline.findIndex(s => !s.done);
|
|
75
|
+
const isActive = task && task.status !== 'completed' && task.status !== 'failed' && task.status !== 'expired' && task.status !== 'cancelled';
|
|
67
76
|
|
|
68
77
|
const duration = task?.started_at && task?.completed_at
|
|
69
78
|
? Math.round((new Date(task.completed_at).getTime() - new Date(task.started_at).getTime()) / 1000)
|
|
@@ -71,7 +80,7 @@ export function TaskDrawer({ taskId, onClose }: TaskDrawerProps) {
|
|
|
71
80
|
|
|
72
81
|
return (
|
|
73
82
|
<>
|
|
74
|
-
<div className="fixed inset-0 bg-black/30 z-40" onClick={onClose} />
|
|
83
|
+
<div className="fixed inset-0 bg-black/30 z-40 anet-fade-in" onClick={onClose} />
|
|
75
84
|
<div className="fixed top-0 right-0 h-full w-full lg:w-[500px] bg-[#0a0a1a] border-l border-[#2a2a4a] z-50 flex flex-col shadow-2xl shadow-black/60 overflow-y-auto animate-slide-in">
|
|
76
85
|
{/* Header */}
|
|
77
86
|
<div className="flex items-center justify-between px-5 py-4 border-b border-[#2a2a4a] bg-[#0d0d1a] sticky top-0">
|
|
@@ -105,29 +114,68 @@ export function TaskDrawer({ taskId, onClose }: TaskDrawerProps) {
|
|
|
105
114
|
)}
|
|
106
115
|
</div>
|
|
107
116
|
|
|
108
|
-
{/* From → To
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
<
|
|
117
|
+
{/* From → To — round 41: use AliasAvatar so from/to colours
|
|
118
|
+
match the rest of the app instead of the legacy blue/cyan
|
|
119
|
+
hardcode. */}
|
|
120
|
+
<div className="flex items-center gap-2 text-sm flex-wrap">
|
|
121
|
+
{task.from_name && <AliasAvatar alias={task.from_name} size={18} />}
|
|
122
|
+
<span className="text-gray-200 font-medium">{task.from_name || '--'}</span>
|
|
123
|
+
<span className="text-gray-600">→</span>
|
|
124
|
+
{task.to_name && <AliasAvatar alias={task.to_name} size={18} />}
|
|
125
|
+
<span className="text-gray-200 font-medium">{task.to_name || '--'}</span>
|
|
113
126
|
</div>
|
|
114
127
|
|
|
115
|
-
{/* Timeline
|
|
128
|
+
{/* Timeline — round 36 polish: relative timestamps, current-step
|
|
129
|
+
pulse if task is still in flight, full ISO in title=. */}
|
|
116
130
|
<div className="bg-[#111128] border border-[#2a2a4a] rounded-xl p-4">
|
|
117
|
-
<div className="
|
|
131
|
+
<div className="flex items-center justify-between mb-3">
|
|
132
|
+
<div className="text-xs text-gray-500 uppercase tracking-wide">Timeline</div>
|
|
133
|
+
{duration !== null && (
|
|
134
|
+
<div className="text-[10px] text-gray-600">
|
|
135
|
+
{duration < 60 ? `${duration}s` : `${Math.floor(duration/60)}m ${duration%60}s`} runtime
|
|
136
|
+
</div>
|
|
137
|
+
)}
|
|
138
|
+
</div>
|
|
118
139
|
<div className="space-y-3">
|
|
119
|
-
{timeline.map((step, i) =>
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
140
|
+
{timeline.map((step, i) => {
|
|
141
|
+
const isCurrent = isActive && i === currentStepIdx;
|
|
142
|
+
const isNextDone = !!timeline[i+1]?.done;
|
|
143
|
+
return (
|
|
144
|
+
<div key={step.key} className="flex items-start gap-3">
|
|
145
|
+
<div className="flex flex-col items-center pt-0.5">
|
|
146
|
+
<span
|
|
147
|
+
className={`relative w-3 h-3 rounded-full shrink-0 ${
|
|
148
|
+
step.done ? step.color : 'bg-gray-700'
|
|
149
|
+
}`}
|
|
150
|
+
>
|
|
151
|
+
{isCurrent && (
|
|
152
|
+
<span
|
|
153
|
+
aria-hidden
|
|
154
|
+
className="absolute -inset-1 rounded-full border border-current opacity-60 anet-current-step-pulse"
|
|
155
|
+
style={{ borderColor: 'currentColor' }}
|
|
156
|
+
/>
|
|
157
|
+
)}
|
|
158
|
+
</span>
|
|
159
|
+
{i < timeline.length - 1 && (
|
|
160
|
+
<div className={`w-0.5 h-5 mt-1 ${
|
|
161
|
+
step.done && isNextDone ? 'bg-gray-500' : step.done ? 'bg-gradient-to-b from-gray-500 to-gray-800' : 'bg-gray-800'
|
|
162
|
+
}`} />
|
|
163
|
+
)}
|
|
164
|
+
</div>
|
|
165
|
+
<div className="flex-1 min-w-0">
|
|
166
|
+
<div className={`text-xs font-medium flex items-center gap-2 ${
|
|
167
|
+
step.done ? 'text-gray-200' : isCurrent ? 'text-cyan-300' : 'text-gray-600'
|
|
168
|
+
}`}>
|
|
169
|
+
<span>{step.label}</span>
|
|
170
|
+
{isCurrent && <span className="text-[9px] uppercase tracking-wide text-cyan-400">in progress</span>}
|
|
171
|
+
</div>
|
|
172
|
+
<div className="text-[10px] text-gray-500" title={step.time || undefined}>
|
|
173
|
+
{step.time ? timeAgo(step.time) : isCurrent ? '—' : 'Pending'}
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
124
176
|
</div>
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
<div className="text-[10px] text-gray-500">{step.time || 'Pending'}</div>
|
|
128
|
-
</div>
|
|
129
|
-
</div>
|
|
130
|
-
))}
|
|
177
|
+
);
|
|
178
|
+
})}
|
|
131
179
|
</div>
|
|
132
180
|
</div>
|
|
133
181
|
|
|
@@ -172,10 +220,6 @@ export function TaskDrawer({ taskId, onClose }: TaskDrawerProps) {
|
|
|
172
220
|
</div>
|
|
173
221
|
)}
|
|
174
222
|
</div>
|
|
175
|
-
<style jsx global>{`
|
|
176
|
-
@keyframes slide-in { from { transform: translateX(100%); } to { transform: translateX(0); } }
|
|
177
|
-
.animate-slide-in { animation: slide-in 0.2s ease-out; }
|
|
178
|
-
`}</style>
|
|
179
223
|
</>
|
|
180
224
|
);
|
|
181
225
|
}
|
|
@@ -33,10 +33,14 @@ const cy = 330;
|
|
|
33
33
|
const onlineRadius = 220;
|
|
34
34
|
const offlineRadius = 315;
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
/** Polar coordinate for a node on a ring. `rotateBy` lets the caller offset
|
|
37
|
+
* the whole ring so two stacked rings don't align radially (round 25: the
|
|
38
|
+
* offline ring gets a half-step rotation so its nodes sit in the gaps
|
|
39
|
+
* between online ones). */
|
|
40
|
+
function polarPoint(index: number, total: number, radius: number, rotateBy = 0) {
|
|
37
41
|
const spread = total <= 2 ? Math.PI : Math.PI * 1.78;
|
|
38
42
|
const start = -Math.PI / 2 - spread / 2;
|
|
39
|
-
const angle = total <= 1 ? -Math.PI / 2 : start + (spread * index) / (total - 1);
|
|
43
|
+
const angle = total <= 1 ? -Math.PI / 2 : start + (spread * index) / (total - 1) + rotateBy;
|
|
40
44
|
return {
|
|
41
45
|
x: cx + radius * Math.cos(angle),
|
|
42
46
|
y: cy + radius * Math.sin(angle),
|
|
@@ -248,8 +252,17 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
248
252
|
positions[s.alias] = polarPoint(index, Math.max(online.length, 1), onlineRadius);
|
|
249
253
|
});
|
|
250
254
|
|
|
255
|
+
// Offset the offline ring radially by half the online step so offline
|
|
256
|
+
// bubbles sit in the angular gaps between online bubbles instead of
|
|
257
|
+
// stacking directly behind them. Also push the outer ring further when
|
|
258
|
+
// there are many offline nodes so labels don't crowd the legend.
|
|
259
|
+
const spread = online.length <= 2 ? Math.PI : Math.PI * 1.78;
|
|
260
|
+
const onlineStep = online.length > 1 ? spread / (online.length - 1) : 0;
|
|
261
|
+
const offlineRotation = online.length > 0 ? onlineStep / 2 : 0;
|
|
262
|
+
const offlineR = offlineRadius + Math.max(0, offline.length - 4) * 6;
|
|
263
|
+
|
|
251
264
|
offline.forEach((s, index) => {
|
|
252
|
-
positions[s.alias] = polarPoint(index, Math.max(offline.length, 1),
|
|
265
|
+
positions[s.alias] = polarPoint(index, Math.max(offline.length, 1), offlineR, offlineRotation);
|
|
253
266
|
});
|
|
254
267
|
|
|
255
268
|
const links = buildFlowLinks(messages, positions);
|
|
@@ -320,15 +333,85 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
320
333
|
<marker id="topo-arrow" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="5" markerHeight="5" orient="auto-start-reverse">
|
|
321
334
|
<path d="M 0 0 L 10 5 L 0 10 z" fill={pal.arrowFill} />
|
|
322
335
|
</marker>
|
|
336
|
+
{/* Round 45: radial radar sweep gradient — bright at center
|
|
337
|
+
(where it meets the hub) fading to leading edge. */}
|
|
338
|
+
<radialGradient id="topo-sweep" cx="0%" cy="50%" r="100%">
|
|
339
|
+
<stop offset="0%" stopColor={isLight ? '#0d9488' : '#22d3ee'} stopOpacity={isLight ? 0.18 : 0.32} />
|
|
340
|
+
<stop offset="70%" stopColor={isLight ? '#0d9488' : '#22d3ee'} stopOpacity={isLight ? 0.10 : 0.18} />
|
|
341
|
+
<stop offset="100%" stopColor={isLight ? '#0d9488' : '#22d3ee'} stopOpacity="0" />
|
|
342
|
+
</radialGradient>
|
|
323
343
|
</defs>
|
|
324
344
|
|
|
325
345
|
<rect width="1000" height="680" fill="url(#topo-panel)" />
|
|
326
346
|
<circle cx={cx} cy={cy} r="330" fill="url(#topo-radar)" />
|
|
327
347
|
|
|
348
|
+
{/* Round 45: subtle star field — 24 deterministic dots scattered
|
|
349
|
+
across the canvas give the radar bg some depth. Skipped on
|
|
350
|
+
light theme so the white surface stays clean. */}
|
|
351
|
+
{!isLight && (
|
|
352
|
+
<g opacity="0.5">
|
|
353
|
+
{Array.from({ length: 28 }).map((_, i) => {
|
|
354
|
+
// Deterministic pseudo-random scatter so positions are
|
|
355
|
+
// stable between renders (no JS hydration mismatch).
|
|
356
|
+
const seed = i * 9301 + 49297;
|
|
357
|
+
const x = ((seed * 13) % 1000);
|
|
358
|
+
const y = ((seed * 7) % 680);
|
|
359
|
+
const r = (i % 3 === 0) ? 1.2 : 0.7;
|
|
360
|
+
return <circle key={i} cx={x} cy={y} r={r} fill="#a5b4fc" opacity={0.35 + (i % 4) * 0.05} />;
|
|
361
|
+
})}
|
|
362
|
+
</g>
|
|
363
|
+
)}
|
|
364
|
+
|
|
365
|
+
{/* Round 45: rotating radar sweep — a 40° wedge with a soft
|
|
366
|
+
leading-edge gradient. Slow 6s rotation reads as a radar
|
|
367
|
+
scan without being noisy. Inline transform-origin on the
|
|
368
|
+
<g> wrapper ensures Chrome / Firefox rotate around (cx,cy)
|
|
369
|
+
instead of the SVG viewBox corner. */}
|
|
370
|
+
<g
|
|
371
|
+
style={{
|
|
372
|
+
transformOrigin: `${cx}px ${cy}px`,
|
|
373
|
+
transformBox: 'view-box',
|
|
374
|
+
}}
|
|
375
|
+
className="anet-topo-sweep"
|
|
376
|
+
opacity={isLight ? 0.7 : 1}
|
|
377
|
+
>
|
|
378
|
+
<path
|
|
379
|
+
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`}
|
|
380
|
+
fill="url(#topo-sweep)"
|
|
381
|
+
/>
|
|
382
|
+
</g>
|
|
383
|
+
|
|
328
384
|
{/* radar rings */}
|
|
329
385
|
{[90, 170, 250, 330].map(radius => (
|
|
330
386
|
<circle key={radius} cx={cx} cy={cy} r={radius} fill="none" stroke={pal.ringStroke} strokeWidth="1" opacity={isLight ? 0.6 : 0.35} />
|
|
331
387
|
))}
|
|
388
|
+
|
|
389
|
+
{/* Round 50: 4 small particles slowly orbiting the outer ring
|
|
390
|
+
(r=330). Each starts at a different angle (offset 0/0.25/0.5/0.75
|
|
391
|
+
of the cycle) so they're evenly spaced. 16s per revolution is
|
|
392
|
+
slow enough to feel ambient, not noisy. Skipped on light theme
|
|
393
|
+
so the white surface stays clean. */}
|
|
394
|
+
{!isLight && [0, 0.25, 0.5, 0.75].map((phase, i) => (
|
|
395
|
+
<g key={`orbit-${i}`}>
|
|
396
|
+
<circle
|
|
397
|
+
cx={cx + 330} cy={cy}
|
|
398
|
+
r={i === 0 ? 2.8 : 2.2}
|
|
399
|
+
fill="#22d3ee"
|
|
400
|
+
opacity={0.9}
|
|
401
|
+
filter="url(#topo-glow)"
|
|
402
|
+
>
|
|
403
|
+
<animateTransform
|
|
404
|
+
attributeName="transform"
|
|
405
|
+
type="rotate"
|
|
406
|
+
from={`${phase * 360} ${cx} ${cy}`}
|
|
407
|
+
to={`${phase * 360 + 360} ${cx} ${cy}`}
|
|
408
|
+
dur="16s"
|
|
409
|
+
repeatCount="indefinite"
|
|
410
|
+
/>
|
|
411
|
+
</circle>
|
|
412
|
+
</g>
|
|
413
|
+
))}
|
|
414
|
+
|
|
332
415
|
{[0, 30, 60, 90, 120, 150].map(angle => (
|
|
333
416
|
<line
|
|
334
417
|
key={angle}
|
|
@@ -342,21 +425,27 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
342
425
|
/>
|
|
343
426
|
))}
|
|
344
427
|
|
|
345
|
-
{/* hub links
|
|
346
|
-
|
|
428
|
+
{/* hub links — round 46: idle spokes now have animated
|
|
429
|
+
stroke-dashoffset so dashes flow outward from the hub
|
|
430
|
+
("command relay" feel). Active spokes carrying live message
|
|
431
|
+
flow stay as solid bright strokes. */}
|
|
432
|
+
{onlineNodes.map((session, idx) => {
|
|
347
433
|
const pos = nodePositions[session.alias];
|
|
348
434
|
if (!pos) return null;
|
|
349
435
|
const path = curvePath({ x: cx, y: cy }, pos, 0);
|
|
436
|
+
const isActiveSpoke = activeAliases.has(session.alias);
|
|
350
437
|
|
|
351
438
|
return (
|
|
352
439
|
<path
|
|
353
440
|
key={`hub-${session.alias}`}
|
|
354
441
|
d={path}
|
|
355
442
|
fill="none"
|
|
356
|
-
stroke={
|
|
357
|
-
strokeWidth={
|
|
358
|
-
strokeDasharray={
|
|
359
|
-
opacity={
|
|
443
|
+
stroke={isActiveSpoke ? pal.spokeStroke.active : pal.spokeStroke.idle}
|
|
444
|
+
strokeWidth={isActiveSpoke ? 2 : 1}
|
|
445
|
+
strokeDasharray={isActiveSpoke ? 'none' : '6 14'}
|
|
446
|
+
opacity={isActiveSpoke ? 0.7 : 0.45}
|
|
447
|
+
className={isActiveSpoke ? undefined : 'anet-topo-spoke-flow'}
|
|
448
|
+
style={isActiveSpoke ? undefined : { animationDelay: `${-(idx * 0.25)}s` }}
|
|
360
449
|
/>
|
|
361
450
|
);
|
|
362
451
|
})}
|
|
@@ -399,27 +488,32 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
399
488
|
);
|
|
400
489
|
})}
|
|
401
490
|
|
|
402
|
-
{/* center hub —
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
logo or label. Closes agent-network#5. */}
|
|
491
|
+
{/* center hub — round 39: enlarged from r=6 (12px) to r=10 (20px)
|
|
492
|
+
core so the "control plane" reads as the network's anchor,
|
|
493
|
+
not a stray particle. Static halo ring at r=18 grounds it
|
|
494
|
+
visually; two outward pulses keep the "signal source" idea
|
|
495
|
+
from r17. Light theme gets a paler core to avoid hot-spotting
|
|
496
|
+
the bg. */}
|
|
409
497
|
<g>
|
|
498
|
+
{/* static grounding halo — sits underneath, low opacity */}
|
|
499
|
+
<circle
|
|
500
|
+
cx={cx} cy={cy} r="18"
|
|
501
|
+
fill={isLight ? '#d1fae5' : '#10b981'}
|
|
502
|
+
opacity={isLight ? 0.35 : 0.10}
|
|
503
|
+
/>
|
|
410
504
|
{/* outer pulse 1 */}
|
|
411
|
-
<circle cx={cx} cy={cy} r="
|
|
412
|
-
<animate attributeName="r" values="
|
|
505
|
+
<circle cx={cx} cy={cy} r="10" fill="none" stroke="#10b981" strokeWidth="1.5">
|
|
506
|
+
<animate attributeName="r" values="10;38;10" dur="2.4s" repeatCount="indefinite" />
|
|
413
507
|
<animate attributeName="opacity" values="0.55;0;0.55" dur="2.4s" repeatCount="indefinite" />
|
|
414
508
|
</circle>
|
|
415
509
|
{/* outer pulse 2 (delayed 1.2s) */}
|
|
416
|
-
<circle cx={cx} cy={cy} r="
|
|
417
|
-
<animate attributeName="r" values="
|
|
510
|
+
<circle cx={cx} cy={cy} r="10" fill="none" stroke="#10b981" strokeWidth="1.5" opacity="0">
|
|
511
|
+
<animate attributeName="r" values="10;38;10" dur="2.4s" begin="1.2s" repeatCount="indefinite" />
|
|
418
512
|
<animate attributeName="opacity" values="0.45;0;0.45" dur="2.4s" begin="1.2s" repeatCount="indefinite" />
|
|
419
513
|
</circle>
|
|
420
|
-
{/* core
|
|
421
|
-
<circle cx={cx} cy={cy} r="
|
|
422
|
-
<circle cx={cx} cy={cy} r="
|
|
514
|
+
{/* core — 20px diameter, larger inner highlight reads as a "lit lamp" */}
|
|
515
|
+
<circle cx={cx} cy={cy} r="10" fill={isLight ? '#059669' : '#10b981'} />
|
|
516
|
+
<circle cx={cx} cy={cy} r="5" fill="#d1fae5" opacity="0.9" />
|
|
423
517
|
</g>
|
|
424
518
|
|
|
425
519
|
{/* agent nodes */}
|
|
@@ -441,7 +535,7 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
441
535
|
<animate attributeName="opacity" values={isLight ? '0.12;0.02;0.12' : '0.18;0.04;0.18'} dur="2.4s" repeatCount="indefinite" />
|
|
442
536
|
</circle>
|
|
443
537
|
)}
|
|
444
|
-
<circle cx={pos.x} cy={pos.y} r={radius + 8} fill={status.halo} opacity={isOnline ? (isLight ? 0.85 : 0.
|
|
538
|
+
<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
539
|
<circle
|
|
446
540
|
cx={pos.x}
|
|
447
541
|
cy={pos.y}
|
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,63 @@
|
|
|
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
|
+
/** Solid bar segment background for the Tasks distribution bar. */
|
|
53
|
+
export const STATUS_BAR_CLASS: Record<string, string> = {
|
|
54
|
+
created: 'bg-gray-500',
|
|
55
|
+
delivered: 'bg-blue-500',
|
|
56
|
+
acked: 'bg-cyan-500',
|
|
57
|
+
running: 'bg-green-500',
|
|
58
|
+
replied: 'bg-purple-500',
|
|
59
|
+
closed: 'bg-gray-600',
|
|
60
|
+
failed: 'bg-red-500',
|
|
61
|
+
cancelled: 'bg-yellow-500',
|
|
62
|
+
expired: 'bg-orange-500',
|
|
63
|
+
};
|
|
@@ -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;
|