@sleep2agi/agent-network-dashboard 0.4.5-preview.9 → 0.4.5-preview.91
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/next-font-manifest.json +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/next-font-manifest.json +1 -1
- 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 +15 -15
- package/.next/server/app/admin.segments/_full.segment.rsc +15 -15
- 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 +3 -3
- 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 +15 -15
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/index.segments/_full.segment.rsc +15 -15
- 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 +3 -3
- package/.next/server/app/login/page/next-font-manifest.json +1 -1
- 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 +15 -15
- package/.next/server/app/login.segments/_full.segment.rsc +15 -15
- 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 +3 -3
- 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/next-font-manifest.json +1 -1
- 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 +15 -15
- package/.next/server/app/logs.segments/_full.segment.rsc +15 -15
- 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 +3 -3
- 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/next-font-manifest.json +1 -1
- 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 +15 -15
- package/.next/server/app/messages.segments/_full.segment.rsc +15 -15
- 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 +3 -3
- 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/next-font-manifest.json +1 -1
- 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 +15 -15
- package/.next/server/app/node.segments/_full.segment.rsc +15 -15
- 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 +3 -3
- 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/next-font-manifest.json +1 -1
- 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 +15 -15
- package/.next/server/app/nodes.segments/_full.segment.rsc +15 -15
- 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 +3 -3
- 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/next-font-manifest.json +1 -1
- 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/next-font-manifest.json +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 +15 -15
- package/.next/server/app/server-logs.segments/_full.segment.rsc +15 -15
- 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 +3 -3
- 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/next-font-manifest.json +1 -1
- 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 +15 -15
- package/.next/server/app/settings/networks.segments/_full.segment.rsc +15 -15
- 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 +3 -3
- 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/next-font-manifest.json +1 -1
- 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/next-font-manifest.json +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 +15 -15
- package/.next/server/app/settings/tokens.segments/_full.segment.rsc +15 -15
- 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 +3 -3
- 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 +15 -15
- package/.next/server/app/settings.segments/_full.segment.rsc +15 -15
- 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 +3 -3
- 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/next-font-manifest.json +1 -1
- 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/next-font-manifest.json +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 +15 -15
- package/.next/server/app/tasks.segments/_full.segment.rsc +15 -15
- 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 +3 -3
- 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/next-font-manifest.js +1 -1
- package/.next/server/next-font-manifest.json +14 -14
- 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/0-hlcuoemhe7w.js +7 -0
- package/.next/static/chunks/02_zpo7taex9o.js +7 -0
- package/.next/static/chunks/03b6jkic-1r9u.css +2 -0
- package/.next/static/chunks/06fliypy0.f0r.js +1 -0
- package/.next/static/chunks/06n3d~fbbtagk.js +1 -0
- package/.next/static/chunks/07eba1lz2u3p4.js +1 -0
- package/.next/static/chunks/0av2~0.96~d~g.js +1 -0
- package/.next/static/chunks/{07okhs5561y5y.js → 0c1bp~g7.8ru..js} +1 -1
- package/.next/static/chunks/0g9aof0ob_fcj.js +1 -0
- package/.next/static/chunks/0gtbwcilfu6b7.js +1 -0
- package/.next/static/chunks/0s8jo6jypzkx5.js +1 -0
- package/.next/static/chunks/0ubqqhhpsaez4.js +1 -0
- package/.next/static/chunks/0vmfu_gz9tp4f.js +1 -0
- package/.next/static/chunks/0~qw9kxksr1v2.js +1 -0
- package/.next/static/chunks/12gprw3l0kvvf.js +1 -0
- package/.next/static/chunks/12mjtyh-j74~k.js +1 -0
- package/.next/static/chunks/12yuo353giomx.js +1 -0
- package/.next/static/media/4fa387ec64143e14-s.0.qu-9752pffj.woff2 +0 -0
- package/.next/static/media/5ce348bf30bf5439-s.0ee55_hj9qcer.woff2 +0 -0
- package/.next/static/media/6306c77e7c8268e4-s.0mao5jbfbduzp.woff2 +0 -0
- package/.next/static/media/797e433ab948586e-s.p.09zddjkbdep5a.woff2 +0 -0
- package/.next/static/media/7d817b4c03b0c5f1-s.0uzt.a6d44yda.woff2 +0 -0
- package/.next/static/media/bbc41e54d2fcbd21-s.0mvwgmnhv29no.woff2 +0 -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 +127 -12
- 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 +81 -44
- 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 +527 -41
- 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 +103 -32
- 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/public/intern_avatar.png +0 -0
- package/scripts/empty-state-test.mjs +41 -0
- package/scripts/fold-only.mjs +47 -0
- package/scripts/intern-alias-test.mjs +46 -0
- package/scripts/loop-review.mjs +270 -0
- package/scripts/network-derive-shot.mjs +16 -0
- package/scripts/network-derive-test.mjs +28 -0
- package/scripts/topo-brand-shot.mjs +61 -0
- package/scripts/topo-cluster-test.mjs +41 -0
- package/scripts/topo-controls-shot.mjs +24 -0
- package/scripts/topo-group-test.mjs +37 -0
- package/scripts/topo-inspect.mjs +56 -0
- package/scripts/topo-interaction-test.mjs +80 -0
- package/scripts/topo-mock-large.mjs +58 -0
- package/scripts/topo-r104-test.mjs +54 -0
- package/scripts/topo-shot.mjs +31 -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/0-.hjh--kcdiy.js +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/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/.next/static/media/4fa387ec64143e14-s.0q3udbd2bu5yp.woff2 +0 -0
- package/.next/static/media/797e433ab948586e-s.p.0.q-h669a_dqa.woff2 +0 -0
- package/.next/static/media/bbc41e54d2fcbd21-s.0gw~uztddq1df.woff2 +0 -0
- 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/{stSZtBF9uGYc3L399m0h1 → u-HIIfy-O0UVQ02APphkX}/_buildManifest.js +0 -0
- /package/.next/static/{stSZtBF9uGYc3L399m0h1 → u-HIIfy-O0UVQ02APphkX}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{stSZtBF9uGYc3L399m0h1 → u-HIIfy-O0UVQ02APphkX}/_ssgManifest.js +0 -0
package/app/server-logs/page.tsx
CHANGED
|
@@ -23,6 +23,30 @@ const LEVEL_BADGE: Record<LogLine['level'], string> = {
|
|
|
23
23
|
error: 'bg-red-500/10 text-red-300 border-red-500/30',
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
+
/** Left-rail strip color per log level — gives the eye a vertical guide
|
|
27
|
+
* when scanning long log walls. Plain hex so Tailwind purge can't drop it. */
|
|
28
|
+
const LEVEL_STRIPE: Record<LogLine['level'], string> = {
|
|
29
|
+
log: 'transparent',
|
|
30
|
+
info: '#22d3ee',
|
|
31
|
+
warn: '#f59e0b',
|
|
32
|
+
error: '#ef4444',
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/** Highlight search-matched substring inside a log line. Case-insensitive
|
|
36
|
+
* first-match wrap; mark inherits color so log-level coloring stays. */
|
|
37
|
+
function highlightSearch(line: string, q: string) {
|
|
38
|
+
if (!q) return line;
|
|
39
|
+
const idx = line.toLowerCase().indexOf(q.toLowerCase());
|
|
40
|
+
if (idx < 0) return line;
|
|
41
|
+
return (
|
|
42
|
+
<>
|
|
43
|
+
{line.slice(0, idx)}
|
|
44
|
+
<mark className="bg-yellow-500/30 text-yellow-100 rounded-sm px-0.5">{line.slice(idx, idx + q.length)}</mark>
|
|
45
|
+
{line.slice(idx + q.length)}
|
|
46
|
+
</>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
26
50
|
function shortTime(iso: string) {
|
|
27
51
|
try {
|
|
28
52
|
const d = new Date(iso);
|
|
@@ -93,49 +117,86 @@ export default function ServerLogsPage() {
|
|
|
93
117
|
|
|
94
118
|
return (
|
|
95
119
|
<div className="p-6 max-w-[1600px] mx-auto">
|
|
96
|
-
<div className="flex items-
|
|
97
|
-
<div className="flex items-
|
|
120
|
+
<div className="flex items-center justify-between mb-4 gap-3">
|
|
121
|
+
<div className="flex items-center gap-3 min-w-0">
|
|
98
122
|
<h1 className="text-2xl font-bold text-white">Server Logs</h1>
|
|
99
|
-
|
|
123
|
+
{/* Round 86: dropped the {logs.length} header chip — r84 added
|
|
124
|
+
`all <count>` to the filter strip just below, so this duplicated
|
|
125
|
+
the value within 40px of itself. */}
|
|
126
|
+
<span className="text-xs text-gray-500 hidden sm:inline">CommHub stdout/stderr</span>
|
|
100
127
|
</div>
|
|
101
|
-
<div className="flex items-center gap-2 text-xs">
|
|
128
|
+
<div className="flex items-center gap-2 text-xs shrink-0">
|
|
102
129
|
<button
|
|
103
130
|
onClick={() => setPaused(p => !p)}
|
|
104
|
-
className={`px-3 py-1 rounded border ${paused ? 'bg-amber-500/10 text-amber-300 border-amber-500/30' : 'bg-cyan-500/10 text-cyan-300 border-cyan-500/30'}`}
|
|
131
|
+
className={`flex items-center gap-1.5 px-3 py-1 rounded border ${paused ? 'bg-amber-500/10 text-amber-300 border-amber-500/30' : 'bg-cyan-500/10 text-cyan-300 border-cyan-500/30'}`}
|
|
105
132
|
>
|
|
106
|
-
{paused ?
|
|
133
|
+
{paused ? (
|
|
134
|
+
<>
|
|
135
|
+
<svg width="10" height="10" viewBox="0 0 24 24" fill="currentColor" aria-hidden><path d="M8 5v14l11-7z" /></svg>
|
|
136
|
+
Resume
|
|
137
|
+
</>
|
|
138
|
+
) : (
|
|
139
|
+
<>
|
|
140
|
+
<svg width="10" height="10" viewBox="0 0 24 24" fill="currentColor" aria-hidden>
|
|
141
|
+
<rect x="6" y="5" width="4" height="14" rx="0.5" />
|
|
142
|
+
<rect x="14" y="5" width="4" height="14" rx="0.5" />
|
|
143
|
+
</svg>
|
|
144
|
+
Pause
|
|
145
|
+
</>
|
|
146
|
+
)}
|
|
107
147
|
</button>
|
|
108
148
|
<label className="flex items-center gap-1 text-gray-400 select-none">
|
|
109
149
|
<input type="checkbox" checked={autoRefresh} onChange={e => setAutoRefresh(e.target.checked)} className="accent-cyan-500" />
|
|
110
|
-
auto-refresh
|
|
150
|
+
<span className="hidden sm:inline">auto-refresh</span>
|
|
111
151
|
</label>
|
|
112
|
-
<button onClick={() => fetchLogs(true)} className="px-3 py-1 rounded border bg-gray-500/10 text-gray-300 border-gray-500/20 hover:bg-gray-500/20">
|
|
113
|
-
|
|
152
|
+
<button onClick={() => fetchLogs(true)} className="flex items-center gap-1.5 px-3 py-1 rounded border bg-gray-500/10 text-gray-300 border-gray-500/20 hover:bg-gray-500/20">
|
|
153
|
+
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden>
|
|
154
|
+
<path d="M3 12a9 9 0 0 1 15.5-6.4L21 8" />
|
|
155
|
+
<path d="M21 3v5h-5" />
|
|
156
|
+
<path d="M21 12a9 9 0 0 1-15.5 6.4L3 16" />
|
|
157
|
+
<path d="M3 21v-5h5" />
|
|
158
|
+
</svg>
|
|
159
|
+
<span className="hidden sm:inline">Reload</span>
|
|
114
160
|
</button>
|
|
115
161
|
</div>
|
|
116
162
|
</div>
|
|
117
163
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
164
|
+
{/* Round 95: flex-wrap + min-w-0 on the search wrapper so the
|
|
165
|
+
search input drops below the chip strip on mobile instead of
|
|
166
|
+
overflowing past the viewport. Desktop layout (single row)
|
|
167
|
+
unchanged because the row has room. */}
|
|
168
|
+
<div className="flex flex-wrap items-center gap-3 mb-3">
|
|
169
|
+
<div className="flex items-center gap-1 text-xs flex-wrap">
|
|
170
|
+
{/* Round 84: every chip shows its count (matches Tasks r56 +
|
|
171
|
+
Audit Log r43); zero-count non-active chips are disabled
|
|
172
|
+
+ muted so the eye lands on the levels that actually have
|
|
173
|
+
entries. */}
|
|
174
|
+
{(['all', 'log', 'info', 'warn', 'error'] as const).map(lv => {
|
|
175
|
+
const count = lv === 'all' ? logs.length : counts[lv as LogLine['level']];
|
|
176
|
+
const isActive = filterLevel === lv;
|
|
177
|
+
const isEmpty = lv !== 'all' && count === 0 && !isActive;
|
|
178
|
+
return (
|
|
179
|
+
<button
|
|
180
|
+
key={lv}
|
|
181
|
+
onClick={() => setFilterLevel(lv)}
|
|
182
|
+
disabled={isEmpty}
|
|
183
|
+
className={`px-2.5 py-1 rounded border flex items-center gap-1 disabled:opacity-30 disabled:cursor-not-allowed ${
|
|
184
|
+
isActive
|
|
185
|
+
? 'bg-cyan-500/15 text-cyan-200 border-cyan-500/40'
|
|
186
|
+
: 'bg-[#11111c] text-gray-400 border-[#2a2a4a] hover:bg-[#1a1a2a]'
|
|
187
|
+
}`}
|
|
188
|
+
>
|
|
189
|
+
<span>{lv}</span>
|
|
190
|
+
<span className={`text-[10px] tabular-nums ${isActive ? 'opacity-80' : 'text-gray-600'}`}>{count}</span>
|
|
191
|
+
</button>
|
|
192
|
+
);
|
|
193
|
+
})}
|
|
133
194
|
</div>
|
|
134
195
|
<input
|
|
135
196
|
value={search}
|
|
136
197
|
onChange={e => setSearch(e.target.value)}
|
|
137
198
|
placeholder="搜索关键字 (alias / task_id / error message)"
|
|
138
|
-
className="flex-1 px-3 py-1.5 text-xs bg-[#11111c] border border-[#2a2a4a] rounded text-gray-200 focus:outline-none focus:border-cyan-500/40"
|
|
199
|
+
className="flex-1 min-w-[140px] basis-full sm:basis-0 px-3 py-1.5 text-xs bg-[#11111c] border border-[#2a2a4a] rounded text-gray-200 focus:outline-none focus:border-cyan-500/40"
|
|
139
200
|
/>
|
|
140
201
|
<span className="text-[10px] text-gray-600">
|
|
141
202
|
{filtered.length} / {logs.length}
|
|
@@ -182,12 +243,27 @@ export default function ServerLogsPage() {
|
|
|
182
243
|
) : (
|
|
183
244
|
<div className="divide-y divide-[#0d0d18] max-h-[calc(100vh-260px)] overflow-y-auto">
|
|
184
245
|
{filtered.map((l, i) => (
|
|
185
|
-
<div key={`${l.ts}-${i}`} className="
|
|
186
|
-
|
|
246
|
+
<div key={`${l.ts}-${i}`} className="relative pl-3 pr-3 py-1 hover:bg-[#0a0a14] flex gap-3">
|
|
247
|
+
{/* Round 32: 2px left rail keyed to level. Makes warn/error
|
|
248
|
+
spike visible in a wall of `log` lines without users having
|
|
249
|
+
to read the level chip on every row. */}
|
|
250
|
+
<span
|
|
251
|
+
aria-hidden
|
|
252
|
+
className="absolute left-0 top-0 bottom-0 w-0.5"
|
|
253
|
+
style={{ backgroundColor: LEVEL_STRIPE[l.level] }}
|
|
254
|
+
/>
|
|
255
|
+
<span className="text-gray-600 shrink-0 w-[100px] text-[10px] tabular-nums">{shortTime(l.ts)}</span>
|
|
187
256
|
<span className={`shrink-0 px-1.5 rounded border text-[9px] uppercase ${LEVEL_BADGE[l.level]}`}>
|
|
188
257
|
{l.level}
|
|
189
258
|
</span>
|
|
190
|
-
|
|
259
|
+
{/* Round 85: CommHub stamps each log line with a `[HH:MM:SS]`
|
|
260
|
+
prefix, but the row already shows shortTime(l.ts) in its
|
|
261
|
+
own 100px column on the left. Strip the duplicate
|
|
262
|
+
prefix at display time so the message starts directly
|
|
263
|
+
with the alias / agent / action. */}
|
|
264
|
+
<span className={`break-all whitespace-pre-wrap ${LEVEL_COLOR[l.level]}`}>
|
|
265
|
+
{highlightSearch(l.line.replace(/^\[\d{2}:\d{2}:\d{2}\]\s*/, ''), search)}
|
|
266
|
+
</span>
|
|
191
267
|
</div>
|
|
192
268
|
))}
|
|
193
269
|
</div>
|
|
@@ -4,6 +4,7 @@ import { useEffect, useState, useCallback } from 'react';
|
|
|
4
4
|
import Link from 'next/link';
|
|
5
5
|
import { timeAgo } from '../../components/utils';
|
|
6
6
|
import { EmptyState } from '../../components/EmptyState';
|
|
7
|
+
import { AliasAvatar } from '../../components/AliasAvatar';
|
|
7
8
|
import useSWR from 'swr';
|
|
8
9
|
|
|
9
10
|
const fetcher = (url: string) => fetch(url).then(r => r.json());
|
|
@@ -99,9 +100,9 @@ export default function NetworksPage() {
|
|
|
99
100
|
<div className="flex items-center gap-4 mb-6">
|
|
100
101
|
<Link href="/settings" className="text-gray-500 hover:text-gray-300 text-sm lg:ml-0 ml-10">← Settings</Link>
|
|
101
102
|
<h1 className="text-2xl font-bold text-white">Networks</h1>
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
103
|
+
{/* Round 93: dropped {networks.length} header chip — the
|
|
104
|
+
My Networks panel below already carries `<N> total` in its
|
|
105
|
+
own subhead. Matches r86/r87 pattern. */}
|
|
105
106
|
</div>
|
|
106
107
|
|
|
107
108
|
<div className="max-w-2xl space-y-6">
|
|
@@ -123,7 +124,12 @@ export default function NetworksPage() {
|
|
|
123
124
|
|
|
124
125
|
{/* List */}
|
|
125
126
|
<section className="bg-[#111128] border border-[#2a2a4a] rounded-xl p-5">
|
|
126
|
-
<
|
|
127
|
+
<div className="flex items-center justify-between mb-3">
|
|
128
|
+
<h2 className="text-sm font-semibold text-gray-300">My Networks</h2>
|
|
129
|
+
{networks.length > 0 && (
|
|
130
|
+
<span className="text-[10px] text-gray-600 uppercase tracking-wide">{networks.length} total</span>
|
|
131
|
+
)}
|
|
132
|
+
</div>
|
|
127
133
|
{loading ? (
|
|
128
134
|
<div className="animate-pulse space-y-2">{[1,2].map(i => <div key={i} className="h-14 bg-gray-800/20 rounded" />)}</div>
|
|
129
135
|
) : networks.length === 0 ? (
|
|
@@ -136,11 +142,14 @@ export default function NetworksPage() {
|
|
|
136
142
|
<div className="space-y-3">
|
|
137
143
|
{networks.map(n => (
|
|
138
144
|
<div key={n.network_id} className="bg-[#0a0a15] rounded-lg px-4 py-3 border border-[#1a1a2a]">
|
|
139
|
-
<div className="flex items-
|
|
140
|
-
<div className="min-w-0">
|
|
141
|
-
<
|
|
142
|
-
<div className="
|
|
143
|
-
|
|
145
|
+
<div className="flex items-start justify-between gap-3">
|
|
146
|
+
<div className="flex items-start gap-3 min-w-0">
|
|
147
|
+
<AliasAvatar alias={n.network_name} size={28} />
|
|
148
|
+
<div className="min-w-0">
|
|
149
|
+
<div className="text-sm text-white font-medium truncate">{n.network_name}</div>
|
|
150
|
+
<div className="text-[10px] text-gray-500 font-mono truncate" title={n.network_id}>{n.network_id} · {timeAgo(n.created_at)}</div>
|
|
151
|
+
{n.description && <div className="text-xs text-gray-400 mt-1">{n.description}</div>}
|
|
152
|
+
</div>
|
|
144
153
|
</div>
|
|
145
154
|
<div className="flex gap-2 shrink-0">
|
|
146
155
|
<button onClick={() => {
|
|
@@ -169,14 +178,20 @@ export default function NetworksPage() {
|
|
|
169
178
|
{(members[n.network_id] || []).length === 0 ? (
|
|
170
179
|
<div className="text-[10px] text-gray-600">No members or V3 auth required</div>
|
|
171
180
|
) : (
|
|
172
|
-
(members[n.network_id] || []).map(m =>
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
181
|
+
(members[n.network_id] || []).map(m => {
|
|
182
|
+
const name = m.username || m.display_name || '?';
|
|
183
|
+
return (
|
|
184
|
+
<div key={m.user_id} className="flex items-center gap-2 justify-between text-xs py-1">
|
|
185
|
+
<div className="flex items-center gap-2 min-w-0">
|
|
186
|
+
<AliasAvatar alias={name} size={16} />
|
|
187
|
+
<span className="text-gray-300 truncate">{name}</span>
|
|
188
|
+
</div>
|
|
189
|
+
<span className={`px-1.5 py-0.5 rounded border text-[10px] shrink-0 ${
|
|
176
190
|
m.role === 'owner' ? 'text-yellow-300 border-yellow-500/20' : 'text-gray-400 border-gray-600/20'
|
|
177
191
|
}`}>{m.role}</span>
|
|
178
192
|
</div>
|
|
179
|
-
|
|
193
|
+
);
|
|
194
|
+
})
|
|
180
195
|
)}
|
|
181
196
|
|
|
182
197
|
{/* Invite */}
|
|
@@ -196,8 +211,17 @@ export default function NetworksPage() {
|
|
|
196
211
|
</div>
|
|
197
212
|
{inviteCode && (
|
|
198
213
|
<div className="mt-2 bg-green-500/5 border border-green-500/20 rounded px-3 py-2">
|
|
199
|
-
<div className="
|
|
200
|
-
|
|
214
|
+
<div className="flex items-center justify-between mb-1">
|
|
215
|
+
<div className="text-[10px] text-green-400 uppercase tracking-wide">Invite code</div>
|
|
216
|
+
<button
|
|
217
|
+
type="button"
|
|
218
|
+
onClick={() => { navigator.clipboard?.writeText(inviteCode).catch(() => {}); }}
|
|
219
|
+
className="text-[10px] text-green-300 hover:text-green-200 px-1.5 py-0.5 rounded border border-green-500/20 hover:bg-green-500/10"
|
|
220
|
+
>
|
|
221
|
+
Copy
|
|
222
|
+
</button>
|
|
223
|
+
</div>
|
|
224
|
+
<code className="text-xs text-green-300 select-all break-all">{inviteCode}</code>
|
|
201
225
|
</div>
|
|
202
226
|
)}
|
|
203
227
|
</div>
|
package/app/settings/page.tsx
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { useState } from 'react';
|
|
4
4
|
import Link from 'next/link';
|
|
5
5
|
import { useAnetConfig, useHealth, useLicense } from '../lib/hooks';
|
|
6
|
+
import { DASHBOARD_VERSION } from '../lib/version';
|
|
6
7
|
|
|
7
8
|
export default function SettingsPage() {
|
|
8
9
|
const { config } = useAnetConfig();
|
|
@@ -18,12 +19,31 @@ export default function SettingsPage() {
|
|
|
18
19
|
|
|
19
20
|
return (
|
|
20
21
|
<div className="min-h-screen bg-[#0a0a1a] text-gray-100 p-4 sm:p-6 font-mono">
|
|
21
|
-
<h1 className="text-2xl font-bold text-white mb-
|
|
22
|
+
<h1 className="text-2xl font-bold text-white mb-3 lg:ml-0 ml-10">Settings</h1>
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
{/* Section anchor nav (round 28) — jump to a group instead of scrolling
|
|
25
|
+
through the whole page. Mirrors the section headers below; pages
|
|
26
|
+
with 7+ cards benefit from a visible table-of-contents. */}
|
|
27
|
+
<nav className="mb-8 flex flex-wrap gap-1 text-xs">
|
|
28
|
+
{[
|
|
29
|
+
{ href: '#connection', label: 'Connection' },
|
|
30
|
+
{ href: '#account', label: 'Account' },
|
|
31
|
+
{ href: '#resources', label: 'Resources' },
|
|
32
|
+
].map(a => (
|
|
33
|
+
<a key={a.href} href={a.href}
|
|
34
|
+
className="rounded-md px-2.5 py-1 text-gray-500 hover:text-cyan-300 hover:bg-cyan-500/10 transition-colors">
|
|
35
|
+
{a.label}
|
|
36
|
+
</a>
|
|
37
|
+
))}
|
|
38
|
+
</nav>
|
|
39
|
+
|
|
40
|
+
<div className="max-w-2xl space-y-10">
|
|
24
41
|
{/* ── Group: Connection ─────────────────────────────────── */}
|
|
25
|
-
<div className="space-y-4">
|
|
26
|
-
<div className="
|
|
42
|
+
<div id="connection" className="space-y-4 scroll-mt-6">
|
|
43
|
+
<div className="flex items-center gap-2 px-1">
|
|
44
|
+
<div className="text-[11px] uppercase tracking-[0.14em] text-gray-400 font-semibold">Connection</div>
|
|
45
|
+
<div className="flex-1 h-px bg-[#2a2a4a]" />
|
|
46
|
+
</div>
|
|
27
47
|
|
|
28
48
|
{/* CommHub Connection */}
|
|
29
49
|
<section className="bg-[#111128] border border-[#2a2a4a] rounded-xl p-5">
|
|
@@ -72,7 +92,7 @@ export default function SettingsPage() {
|
|
|
72
92
|
<span className={`text-gray-300 ${valueClass}`}>{health?.sessions ?? '--'}</span>
|
|
73
93
|
</div>
|
|
74
94
|
<div className={rowClass}>
|
|
75
|
-
<span className="text-gray-500">SSE
|
|
95
|
+
<span className="text-gray-500">SSE streams <span className="text-gray-600">· server</span></span>
|
|
76
96
|
<span className={`text-gray-300 ${valueClass}`}>{health?.sse_connections ?? '--'}</span>
|
|
77
97
|
</div>
|
|
78
98
|
<div className={rowClass}>
|
|
@@ -88,7 +108,9 @@ export default function SettingsPage() {
|
|
|
88
108
|
<div className="space-y-3 text-sm">
|
|
89
109
|
<div className={rowClass}>
|
|
90
110
|
<span className="text-gray-500">Version</span>
|
|
91
|
-
<span className={`text-gray-300 ${valueClass}`}
|
|
111
|
+
<span className={`text-gray-300 font-mono ${valueClass}`} title="From package.json @sleep2agi/agent-network-dashboard">
|
|
112
|
+
{DASHBOARD_VERSION}
|
|
113
|
+
</span>
|
|
92
114
|
</div>
|
|
93
115
|
<div className={rowClass}>
|
|
94
116
|
<span className="text-gray-500">Data Layer</span>
|
|
@@ -96,15 +118,18 @@ export default function SettingsPage() {
|
|
|
96
118
|
</div>
|
|
97
119
|
<div className={rowClass}>
|
|
98
120
|
<span className="text-gray-500">Pages</span>
|
|
99
|
-
<span className={`text-gray-300 ${valueClass}`}>Overview, Tasks, Nodes, Messages, Settings</span>
|
|
121
|
+
<span className={`text-gray-300 ${valueClass}`}>Overview, Tasks, Nodes, Messages, Networks, Audit, Server Logs, Admin, Settings</span>
|
|
100
122
|
</div>
|
|
101
123
|
</div>
|
|
102
124
|
</section>
|
|
103
125
|
</div>
|
|
104
126
|
|
|
105
127
|
{/* ── Group: Account ────────────────────────────────────── */}
|
|
106
|
-
<div className="space-y-4">
|
|
107
|
-
<div className="
|
|
128
|
+
<div id="account" className="space-y-4 scroll-mt-6">
|
|
129
|
+
<div className="flex items-center gap-2 px-1">
|
|
130
|
+
<div className="text-[11px] uppercase tracking-[0.14em] text-gray-400 font-semibold">Account</div>
|
|
131
|
+
<div className="flex-1 h-px bg-[#2a2a4a]" />
|
|
132
|
+
</div>
|
|
108
133
|
|
|
109
134
|
{/* License */}
|
|
110
135
|
<section className="bg-[#111128] border border-[#2a2a4a] rounded-xl p-5">
|
|
@@ -232,8 +257,11 @@ export default function SettingsPage() {
|
|
|
232
257
|
</div>
|
|
233
258
|
|
|
234
259
|
{/* ── Group: Resources ──────────────────────────────────── */}
|
|
235
|
-
<div className="space-y-4">
|
|
236
|
-
<div className="
|
|
260
|
+
<div id="resources" className="space-y-4 scroll-mt-6">
|
|
261
|
+
<div className="flex items-center gap-2 px-1">
|
|
262
|
+
<div className="text-[11px] uppercase tracking-[0.14em] text-gray-400 font-semibold">Resources</div>
|
|
263
|
+
<div className="flex-1 h-px bg-[#2a2a4a]" />
|
|
264
|
+
</div>
|
|
237
265
|
|
|
238
266
|
{/* API Tokens + Networks */}
|
|
239
267
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
package/app/tasks/[id]/page.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import { use } from 'react';
|
|
|
4
4
|
import Link from 'next/link';
|
|
5
5
|
import useSWR from 'swr';
|
|
6
6
|
import { timeAgo } from '../../components/utils';
|
|
7
|
+
import { STATUS_CHIP_CLASS } from '../../lib/status';
|
|
7
8
|
|
|
8
9
|
const fetcher = async (url: string) => {
|
|
9
10
|
const res = await fetch(url);
|
|
@@ -55,18 +56,6 @@ function Timeline({ steps }: { steps: TimelineStep[] }) {
|
|
|
55
56
|
);
|
|
56
57
|
}
|
|
57
58
|
|
|
58
|
-
const STATUS_COLORS: Record<string, string> = {
|
|
59
|
-
created: 'bg-gray-500/10 text-gray-400 border-gray-500/20',
|
|
60
|
-
delivered: 'bg-blue-500/10 text-blue-300 border-blue-500/20',
|
|
61
|
-
acked: 'bg-cyan-500/10 text-cyan-300 border-cyan-500/20',
|
|
62
|
-
running: 'bg-green-500/10 text-green-300 border-green-500/20',
|
|
63
|
-
replied: 'bg-purple-500/10 text-purple-300 border-purple-500/20',
|
|
64
|
-
closed: 'bg-gray-500/10 text-gray-500 border-gray-500/20',
|
|
65
|
-
failed: 'bg-red-500/10 text-red-300 border-red-500/20',
|
|
66
|
-
cancelled: 'bg-yellow-500/10 text-yellow-300 border-yellow-500/20',
|
|
67
|
-
expired: 'bg-orange-500/10 text-orange-300 border-orange-500/20',
|
|
68
|
-
};
|
|
69
|
-
|
|
70
59
|
export default function TaskDetailPage({ params }: { params: Promise<{ id: string }> }) {
|
|
71
60
|
const { id } = use(params);
|
|
72
61
|
const { data, isLoading } = useSWR(
|
|
@@ -105,7 +94,7 @@ export default function TaskDetailPage({ params }: { params: Promise<{ id: strin
|
|
|
105
94
|
);
|
|
106
95
|
}
|
|
107
96
|
|
|
108
|
-
const statusColor =
|
|
97
|
+
const statusColor = STATUS_CHIP_CLASS[task.status] || STATUS_CHIP_CLASS.created;
|
|
109
98
|
const steps: TimelineStep[] = [
|
|
110
99
|
{ label: 'Created', time: task.created_at, color: 'text-gray-400' },
|
|
111
100
|
{ label: 'Delivered', time: task.delivered_at, color: 'text-blue-400' },
|
package/app/tasks/page.tsx
CHANGED
|
@@ -7,6 +7,8 @@ import { useNetworkId } from '../lib/network-context';
|
|
|
7
7
|
import { TaskDrawer } from '../components/TaskDrawer';
|
|
8
8
|
import { EmptyState } from '../components/EmptyState';
|
|
9
9
|
import { AliasAvatar } from '../components/AliasAvatar';
|
|
10
|
+
import { previewContent } from '../components/utils';
|
|
11
|
+
import { TASK_STATUSES, STATUS_CHIP_CLASS, STATUS_DOT_HEX, STATUS_BAR_CLASS } from '../lib/status';
|
|
10
12
|
|
|
11
13
|
interface Task {
|
|
12
14
|
task_id: string;
|
|
@@ -24,48 +26,8 @@ interface Task {
|
|
|
24
26
|
expires_at: string;
|
|
25
27
|
}
|
|
26
28
|
|
|
27
|
-
const STATUS_OPTIONS = [
|
|
28
|
-
'',
|
|
29
|
-
'created',
|
|
30
|
-
'delivered',
|
|
31
|
-
'acked',
|
|
32
|
-
'running',
|
|
33
|
-
'replied',
|
|
34
|
-
'closed',
|
|
35
|
-
'failed',
|
|
36
|
-
'cancelled',
|
|
37
|
-
'expired',
|
|
38
|
-
];
|
|
39
|
-
|
|
40
|
-
const STATUS_COLORS: Record<string, string> = {
|
|
41
|
-
created: 'bg-gray-500/10 text-gray-400 border-gray-500/20',
|
|
42
|
-
delivered: 'bg-blue-500/10 text-blue-300 border-blue-500/20',
|
|
43
|
-
acked: 'bg-cyan-500/10 text-cyan-300 border-cyan-500/20',
|
|
44
|
-
running: 'bg-green-500/10 text-green-300 border-green-500/20',
|
|
45
|
-
replied: 'bg-purple-500/10 text-purple-300 border-purple-500/20',
|
|
46
|
-
closed: 'bg-gray-500/10 text-gray-500 border-gray-500/20',
|
|
47
|
-
failed: 'bg-red-500/10 text-red-300 border-red-500/20',
|
|
48
|
-
cancelled: 'bg-yellow-500/10 text-yellow-300 border-yellow-500/20',
|
|
49
|
-
expired: 'bg-orange-500/10 text-orange-300 border-orange-500/20',
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
/** Small colored dot per status — visible on every tab (active + inactive)
|
|
53
|
-
* so users can scan the row at a glance. Hex inlined to avoid Tailwind
|
|
54
|
-
* purging dynamic `bg-${family}-400` class names. */
|
|
55
|
-
const STATUS_DOTS: Record<string, string> = {
|
|
56
|
-
created: '#9ca3af',
|
|
57
|
-
delivered: '#60a5fa',
|
|
58
|
-
acked: '#22d3ee',
|
|
59
|
-
running: '#4ade80',
|
|
60
|
-
replied: '#a78bfa',
|
|
61
|
-
closed: '#6b7280',
|
|
62
|
-
failed: '#f87171',
|
|
63
|
-
cancelled: '#facc15',
|
|
64
|
-
expired: '#fb923c',
|
|
65
|
-
};
|
|
66
|
-
|
|
67
29
|
function statusBadge(status: string) {
|
|
68
|
-
const color =
|
|
30
|
+
const color = STATUS_CHIP_CLASS[status] || 'bg-gray-500/10 text-gray-400 border-gray-500/20';
|
|
69
31
|
return `text-xs px-2 py-0.5 rounded-md border ${color}`;
|
|
70
32
|
}
|
|
71
33
|
|
|
@@ -145,11 +107,25 @@ function TasksContent() {
|
|
|
145
107
|
{/* Header */}
|
|
146
108
|
<div className="flex items-center gap-4 mb-6">
|
|
147
109
|
<h1 className="text-2xl font-bold text-white lg:ml-0 ml-10">Tasks</h1>
|
|
148
|
-
|
|
149
|
-
|
|
110
|
+
{/* Round 88: pagination-aware chip. When tasks.length < count
|
|
111
|
+
the API has more rows than the current limit=100 slice;
|
|
112
|
+
show `loaded / total` so users notice. Otherwise just the
|
|
113
|
+
single total — matches r87 /logs pattern. */}
|
|
114
|
+
<span
|
|
115
|
+
className="text-xs bg-blue-900/30 text-blue-400 px-2 py-0.5 rounded-full border border-blue-800/30 tabular-nums"
|
|
116
|
+
title={tasks.length < count ? `Showing ${tasks.length} of ${count} tasks` : undefined}
|
|
117
|
+
>
|
|
118
|
+
{tasks.length < count ? `${tasks.length} / ${count}` : count}
|
|
150
119
|
</span>
|
|
151
120
|
</div>
|
|
152
121
|
|
|
122
|
+
{/* Round 75: hide the status tab strip + From/To filter row when
|
|
123
|
+
there are no tasks at all. Each tab would show "0" and the
|
|
124
|
+
filter inputs would have nothing to act on — pure noise above
|
|
125
|
+
the empty-state CTA. When at least one task exists, the chrome
|
|
126
|
+
comes back even if the current filter happens to hide
|
|
127
|
+
everything (so users can clear filters). */}
|
|
128
|
+
{tasks.length > 0 && <>
|
|
153
129
|
{/* Status Tabs — color-coded dots per status family so users can
|
|
154
130
|
scan the row at a glance. Active tab gets full chip styling
|
|
155
131
|
(bg + border + text color), inactive tabs only carry the dot
|
|
@@ -159,23 +135,39 @@ function TasksContent() {
|
|
|
159
135
|
elements) hinting more content. */}
|
|
160
136
|
<div className="anet-tabstrip-wrap mb-4">
|
|
161
137
|
<div className="anet-tabstrip flex sm:flex-wrap gap-1 bg-[#111128] rounded-lg border border-[#2a2a4a] p-1 overflow-x-auto sm:overflow-x-visible scrollbar-thin">
|
|
162
|
-
{
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
138
|
+
{(() => {
|
|
139
|
+
// Round 56: compute per-status counts so the tab strip doubles as a
|
|
140
|
+
// status distribution dashboard. Matches Audit Log (r43) + Overview
|
|
141
|
+
// agent filter (r34) chip-with-count pattern.
|
|
142
|
+
const counts: Record<string, number> = {};
|
|
143
|
+
tasks.forEach(t => { counts[t.status] = (counts[t.status] || 0) + 1; });
|
|
144
|
+
return ['', ...TASK_STATUSES].map(s => {
|
|
145
|
+
const count = s === '' ? tasks.length : counts[s] || 0;
|
|
146
|
+
const isActive = filterStatus === s;
|
|
147
|
+
return (
|
|
148
|
+
<button
|
|
149
|
+
key={s}
|
|
150
|
+
onClick={() => setFilterStatus(s)}
|
|
151
|
+
disabled={count === 0 && s !== '' && !isActive}
|
|
152
|
+
className={`px-3 py-1.5 rounded-md text-xs transition-colors flex items-center gap-1.5 shrink-0 whitespace-nowrap disabled:opacity-30 disabled:cursor-not-allowed ${
|
|
153
|
+
isActive
|
|
154
|
+
? `${STATUS_CHIP_CLASS[s] || 'bg-cyan-500/10 text-cyan-300 border-cyan-500/20'} border`
|
|
155
|
+
: 'text-gray-500 hover:text-gray-300 hover:bg-[#1a1a2a]/40'
|
|
156
|
+
}`}
|
|
157
|
+
>
|
|
158
|
+
{s && (
|
|
159
|
+
<span
|
|
160
|
+
aria-hidden
|
|
161
|
+
className="inline-block w-1.5 h-1.5 rounded-full shrink-0"
|
|
162
|
+
style={{ backgroundColor: STATUS_DOT_HEX[s] || '#6b7280' }}
|
|
163
|
+
/>
|
|
164
|
+
)}
|
|
165
|
+
<span>{s || 'All'}</span>
|
|
166
|
+
<span className={`text-[10px] tabular-nums ${isActive ? 'opacity-80' : 'text-gray-600'}`}>{count}</span>
|
|
167
|
+
</button>
|
|
168
|
+
);
|
|
169
|
+
});
|
|
170
|
+
})()}
|
|
179
171
|
</div>
|
|
180
172
|
</div>
|
|
181
173
|
|
|
@@ -211,27 +203,28 @@ function TasksContent() {
|
|
|
211
203
|
</button>
|
|
212
204
|
)}
|
|
213
205
|
</div>
|
|
206
|
+
</>}
|
|
214
207
|
|
|
215
208
|
{/* Status distribution */}
|
|
216
209
|
{tasks.length > 0 && !filterStatus && (() => {
|
|
217
210
|
const stats: Record<string, number> = {};
|
|
218
211
|
tasks.forEach(t => { stats[t.status] = (stats[t.status] || 0) + 1; });
|
|
219
212
|
const total = tasks.length || 1;
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
{ key
|
|
225
|
-
|
|
226
|
-
].filter(b => stats[b.key]);
|
|
213
|
+
// Round 67: derive from shared TASK_STATUSES + STATUS_BAR_CLASS in
|
|
214
|
+
// app/lib/status.ts. Adding a new lifecycle status updates badge,
|
|
215
|
+
// tab dots, and bar in one edit.
|
|
216
|
+
const bars = TASK_STATUSES
|
|
217
|
+
.map(key => ({ key, color: STATUS_BAR_CLASS[key] }))
|
|
218
|
+
.filter(b => stats[b.key]);
|
|
227
219
|
if (!bars.length) return null;
|
|
220
|
+
// Round 63: removed the per-status legend row beneath the bar —
|
|
221
|
+
// it duplicated the counts already shown in the tab chip strip
|
|
222
|
+
// since r56. The proportional bar stays because it adds a visual
|
|
223
|
+
// "shape of workload" that the chip numbers don't convey.
|
|
228
224
|
return (
|
|
229
225
|
<div className="mb-6">
|
|
230
226
|
<div className="flex h-2 rounded-full overflow-hidden bg-gray-800">
|
|
231
|
-
{bars.map(b => <div key={b.key} className={b.color} style={{ width: `${(stats[b.key]/total)*100}%` }} />)}
|
|
232
|
-
</div>
|
|
233
|
-
<div className="flex flex-wrap gap-3 mt-1.5 text-[10px] text-gray-500">
|
|
234
|
-
{bars.map(b => <span key={b.key}><span className={`inline-block w-2 h-2 rounded-full ${b.color} mr-1`} />{stats[b.key]} {b.key}</span>)}
|
|
227
|
+
{bars.map(b => <div key={b.key} className={b.color} style={{ width: `${(stats[b.key]/total)*100}%` }} title={`${b.key}: ${stats[b.key]}`} />)}
|
|
235
228
|
</div>
|
|
236
229
|
</div>
|
|
237
230
|
);
|
|
@@ -301,7 +294,7 @@ function TasksContent() {
|
|
|
301
294
|
<span className="truncate text-sm text-gray-200">{t.to_name || '--'}</span>
|
|
302
295
|
</div>
|
|
303
296
|
<div className="col-span-4 text-xs text-gray-400 truncate" title={t.content}>
|
|
304
|
-
{t.content
|
|
297
|
+
{previewContent(t.content)}
|
|
305
298
|
</div>
|
|
306
299
|
<div className="col-span-1">
|
|
307
300
|
<span className={`text-xs ${priorityBadge(t.priority)}`}>{t.priority || 'normal'}</span>
|
|
@@ -340,7 +333,7 @@ function TasksContent() {
|
|
|
340
333
|
{t.to_name && <AliasAvatar alias={t.to_name} size={16} />}
|
|
341
334
|
<span className="truncate max-w-[40%]">{t.to_name || '--'}</span>
|
|
342
335
|
</div>
|
|
343
|
-
<div className="text-xs text-gray-400 line-clamp-1">{t.content
|
|
336
|
+
<div className="text-xs text-gray-400 line-clamp-1" title={t.content}>{previewContent(t.content)}</div>
|
|
344
337
|
<div className="text-xs text-gray-600">{timeAgo(t.created_at)}</div>
|
|
345
338
|
</div>
|
|
346
339
|
|
package/package.json
CHANGED
|
Binary file
|