@sleep2agi/agent-network-dashboard 0.5.7-preview.7 → 0.5.7-preview.73
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/app-path-routes-manifest.json +0 -1
- package/.next/build-manifest.json +3 -3
- package/.next/diagnostics/route-bundle-stats.json +65 -65
- package/.next/fallback-build-manifest.json +3 -3
- package/.next/prerender-manifest.json +3 -3
- package/.next/routes-manifest.json +0 -6
- 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 +2 -1
- package/.next/server/app/_not-found/page.js +1 -1
- package/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +4 -4
- package/.next/server/app/_not-found.rsc +16 -16
- package/.next/server/app/_not-found.segments/_full.segment.rsc +16 -16
- package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/server/app/_not-found.segments/_index.segment.rsc +9 -9
- 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 +3 -3
- package/.next/server/app/admin/page/next-font-manifest.json +2 -1
- package/.next/server/app/admin/page.js +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 +20 -19
- package/.next/server/app/admin.segments/_full.segment.rsc +20 -19
- package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
- package/.next/server/app/admin.segments/_index.segment.rsc +9 -9
- package/.next/server/app/admin.segments/_tree.segment.rsc +5 -4
- 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 +20 -19
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/index.segments/_full.segment.rsc +20 -19
- package/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/server/app/index.segments/_index.segment.rsc +9 -9
- package/.next/server/app/index.segments/_tree.segment.rsc +5 -4
- package/.next/server/app/login/page/next-font-manifest.json +2 -1
- package/.next/server/app/login/page.js +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 +20 -19
- package/.next/server/app/login.segments/_full.segment.rsc +20 -19
- package/.next/server/app/login.segments/_head.segment.rsc +4 -4
- package/.next/server/app/login.segments/_index.segment.rsc +9 -9
- package/.next/server/app/login.segments/_tree.segment.rsc +5 -4
- 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 +2 -1
- package/.next/server/app/logs/page.js +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 +20 -19
- package/.next/server/app/logs.segments/_full.segment.rsc +20 -19
- package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
- package/.next/server/app/logs.segments/_index.segment.rsc +9 -9
- package/.next/server/app/logs.segments/_tree.segment.rsc +5 -4
- 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/manifest.webmanifest.body +1 -1
- package/.next/server/app/messages/page/next-font-manifest.json +2 -1
- package/.next/server/app/messages/page.js +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 +20 -19
- package/.next/server/app/messages.segments/_full.segment.rsc +20 -19
- package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
- package/.next/server/app/messages.segments/_index.segment.rsc +9 -9
- package/.next/server/app/messages.segments/_tree.segment.rsc +5 -4
- 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 +2 -1
- package/.next/server/app/node/page.js +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 +20 -19
- package/.next/server/app/node.segments/_full.segment.rsc +20 -19
- package/.next/server/app/node.segments/_head.segment.rsc +4 -4
- package/.next/server/app/node.segments/_index.segment.rsc +9 -9
- package/.next/server/app/node.segments/_tree.segment.rsc +5 -4
- 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 +2 -1
- package/.next/server/app/nodes/page.js +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 +20 -19
- package/.next/server/app/nodes.segments/_full.segment.rsc +20 -19
- package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
- package/.next/server/app/nodes.segments/_index.segment.rsc +9 -9
- package/.next/server/app/nodes.segments/_tree.segment.rsc +5 -4
- 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 +2 -1
- package/.next/server/app/page.js +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 +2 -1
- package/.next/server/app/server-logs/page.js +1 -1
- package/.next/server/app/server-logs/page.js.nft.json +1 -1
- package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/server-logs.html +4 -4
- package/.next/server/app/server-logs.rsc +20 -19
- package/.next/server/app/server-logs.segments/_full.segment.rsc +20 -19
- package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
- package/.next/server/app/server-logs.segments/_index.segment.rsc +9 -9
- package/.next/server/app/server-logs.segments/_tree.segment.rsc +5 -4
- 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/servers/page/next-font-manifest.json +2 -1
- package/.next/server/app/servers/page.js +1 -1
- package/.next/server/app/servers/page.js.nft.json +1 -1
- package/.next/server/app/servers/page_client-reference-manifest.js +1 -1
- package/.next/server/app/servers.html +4 -4
- package/.next/server/app/servers.rsc +20 -19
- package/.next/server/app/servers.segments/_full.segment.rsc +20 -19
- package/.next/server/app/servers.segments/_head.segment.rsc +4 -4
- package/.next/server/app/servers.segments/_index.segment.rsc +9 -9
- package/.next/server/app/servers.segments/_tree.segment.rsc +5 -4
- package/.next/server/app/servers.segments/servers/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/servers.segments/servers.segment.rsc +3 -3
- package/.next/server/app/settings/networks/page/next-font-manifest.json +2 -1
- package/.next/server/app/settings/networks/page.js +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 +20 -19
- package/.next/server/app/settings/networks.segments/_full.segment.rsc +20 -19
- package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings/networks.segments/_index.segment.rsc +9 -9
- package/.next/server/app/settings/networks.segments/_tree.segment.rsc +5 -4
- 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 +2 -1
- package/.next/server/app/settings/page.js +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 +2 -1
- package/.next/server/app/settings/tokens/page.js +1 -1
- package/.next/server/app/settings/tokens/page.js.nft.json +1 -1
- package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/tokens.html +4 -4
- package/.next/server/app/settings/tokens.rsc +20 -19
- package/.next/server/app/settings/tokens.segments/_full.segment.rsc +20 -19
- package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings/tokens.segments/_index.segment.rsc +9 -9
- package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +5 -4
- 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 +20 -19
- package/.next/server/app/settings.segments/_full.segment.rsc +20 -19
- package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings.segments/_index.segment.rsc +9 -9
- package/.next/server/app/settings.segments/_tree.segment.rsc +5 -4
- 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 +2 -1
- package/.next/server/app/tasks/[id]/page.js +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 +2 -1
- package/.next/server/app/tasks/page.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 +20 -19
- package/.next/server/app/tasks.segments/_full.segment.rsc +20 -19
- package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
- package/.next/server/app/tasks.segments/_index.segment.rsc +9 -9
- package/.next/server/app/tasks.segments/_tree.segment.rsc +5 -4
- 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/app-paths-manifest.json +0 -1
- package/.next/server/chunks/00jm_next_dist_0ju_ux9._.js +1 -1
- package/.next/server/chunks/00jm_next_dist_0ju_ux9._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__030vg4n._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__030vg4n._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__04gz75y._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__04gz75y._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__05kf31s._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__05kf31s._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__096ytyk._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__096ytyk._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0u4-66w._.js +8 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0u4-66w._.js.map +1 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +3 -3
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0_870i8._.js +3 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0_870i8._.js.map +1 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0fjlnh~._.js +3 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0fjlnh~._.js.map +1 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0gd.4pc._.js +9 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0gd.4pc._.js.map +1 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0wn4jc5._.js +3 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0wn4jc5._.js.map +1 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0r7kb.o._.js +9 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0r7kb.o._.js.map +1 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_servers_page_tsx_0jib5qm._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_servers_page_tsx_0jib5qm._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_tasks_page_tsx_0mwxy4z._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_tasks_page_tsx_0mwxy4z._.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 +30 -15
- 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..h8s._z~uek.js +1 -0
- package/.next/static/chunks/0.mh8n0itrii5.js +1 -0
- package/.next/static/chunks/{0jp~cs9-zkmqa.js → 00b4y77vxfabl.js} +1 -1
- package/.next/static/chunks/049vx3qljs1tt.js +1 -0
- package/.next/static/chunks/066jf0nk75nic.css +2 -0
- package/.next/static/chunks/06vp7429lrzl7.js +1 -0
- package/.next/static/chunks/06xxn73qy0qiz.js +7 -0
- package/.next/static/chunks/0_bn~gcrgo.4n.js +1 -0
- package/.next/static/chunks/0_tvbie.c68h5.js +1 -0
- package/.next/static/chunks/0cp0cz3mxejl~.js +4 -0
- package/.next/static/chunks/0g4d-_fi-d9hg.js +1 -0
- package/.next/static/chunks/0scww97p6z5z..css +1 -0
- package/.next/static/chunks/0shtnff1p8hzw.js +1 -0
- package/.next/static/chunks/0wz0122ym_gr3.js +1 -0
- package/.next/static/chunks/0y5gol09tlu63.js +1 -0
- package/.next/static/chunks/13l-ngu707546.js +1 -0
- package/.next/static/chunks/14141xj5.1t3t.js +1 -0
- package/.next/static/chunks/149a4l50_3vw-.js +7 -0
- package/.next/static/chunks/152_p6jt7txp6.js +1 -0
- package/.next/static/chunks/15hos-r_t7doi.js +1 -0
- package/.next/static/media/4fa387ec64143e14-s.0wkzw~je483f-.woff2 +0 -0
- package/.next/static/media/53b9e256198e5412-s.0-wfv7uh4i7h9.woff2 +0 -0
- package/.next/static/media/5ce348bf30bf5439-s.0zgw-jeven.3w.woff2 +0 -0
- package/.next/static/media/6306c77e7c8268e4-s.0rhz0arwfsn~5.woff2 +0 -0
- package/.next/static/media/7178b3e590c64307-s.0nx0ww8fni_q3.woff2 +0 -0
- package/.next/static/media/797e433ab948586e-s.p.08e28id.o-okb.woff2 +0 -0
- package/.next/static/media/7d817b4c03b0c5f1-s.0l76wvqk9d84w.woff2 +0 -0
- package/.next/static/media/8a480f0b521d4e75-s.0jzbimsg8vl84.woff2 +0 -0
- package/.next/static/media/bbc41e54d2fcbd21-s.0k4k9394f2q-k.woff2 +0 -0
- package/.next/static/media/caa3a2e1cccd8315-s.p.09~u27dqhyhd6.woff2 +0 -0
- package/.next/static/media/fef07dbb0973bf53-s.12tyk43_3sh9u.woff2 +0 -0
- package/.next/trace +2 -2
- package/.next/trace-build +1 -1
- package/.next/types/routes.d.ts +1 -2
- package/.next/types/validator.ts +0 -9
- package/app/admin/page.tsx +53 -38
- package/app/components/AgentCard.tsx +36 -20
- package/app/components/AppShell.tsx +7 -1
- package/app/components/ChatPopover.tsx +1 -1
- package/app/components/CollapsibleSearch.tsx +127 -0
- package/app/components/CommandCenter.tsx +14 -4
- package/app/components/CommandPalette.tsx +6 -6
- package/app/components/DispatchPanel.tsx +13 -10
- package/app/components/EmptyState.tsx +19 -4
- package/app/components/HealthBanner.tsx +28 -4
- package/app/components/HelpOverlay.tsx +4 -7
- package/app/components/LoadingSkeleton.tsx +31 -21
- package/app/components/MobileNav.tsx +28 -21
- package/app/components/Sidebar.tsx +30 -23
- package/app/components/StatsBar.tsx +69 -42
- package/app/components/TaskChatPanel.tsx +19 -5
- package/app/components/TaskDrawer.tsx +9 -7
- package/app/components/ThemeSwitcher.tsx +15 -79
- package/app/components/TopoGraph.tsx +31 -21
- package/app/components/UserBar.tsx +5 -5
- package/app/globals.css +1757 -1776
- package/app/layout.tsx +37 -4
- package/app/lib/hooks.ts +0 -5
- package/app/lib/status.ts +24 -17
- package/app/login/page.tsx +7 -7
- package/app/logs/page.tsx +12 -6
- package/app/manifest.ts +2 -2
- package/app/messages/page.tsx +67 -47
- package/app/node/page.tsx +27 -17
- package/app/nodes/page.tsx +62 -49
- package/app/page.tsx +60 -282
- package/app/server-logs/page.tsx +40 -14
- package/app/servers/page.tsx +33 -12
- package/app/settings/networks/page.tsx +17 -15
- package/app/settings/page.tsx +102 -96
- package/app/settings/tokens/page.tsx +5 -5
- package/app/tasks/[id]/page.tsx +10 -10
- package/app/tasks/page.tsx +58 -34
- package/bin/start.js +0 -0
- package/package.json +1 -1
- package/public/favicon.svg +1 -1
- package/public/manifest.webmanifest +24 -0
- package/public/robots.txt +7 -0
- package/.next/server/app/api/hub/license/route/app-paths-manifest.json +0 -3
- package/.next/server/app/api/hub/license/route/build-manifest.json +0 -9
- package/.next/server/app/api/hub/license/route/server-reference-manifest.json +0 -4
- package/.next/server/app/api/hub/license/route.js +0 -7
- package/.next/server/app/api/hub/license/route.js.map +0 -5
- package/.next/server/app/api/hub/license/route.js.nft.json +0 -1
- package/.next/server/app/api/hub/license/route_client-reference-manifest.js +0 -3
- package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_license_route_actions_0a4.fuh.js +0 -3
- package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_license_route_actions_0a4.fuh.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__0rovr5-._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0rovr5-._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0lu1wok._.js +0 -8
- package/.next/server/chunks/ssr/[root-of-the-server]__0lu1wok._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__11fu-5m._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__11fu-5m._.js.map +0 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_057q.ne._.js +0 -3
- package/.next/server/chunks/ssr/agent-network-dashboard_app_057q.ne._.js.map +0 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js +0 -3
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js.map +0 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js +0 -9
- package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js.map +0 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0s5uqlp._.js +0 -9
- package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0s5uqlp._.js.map +0 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_server-logs_page_tsx_0dg.l_8._.js +0 -3
- package/.next/server/chunks/ssr/agent-network-dashboard_app_server-logs_page_tsx_0dg.l_8._.js.map +0 -1
- package/.next/static/chunks/0-mpa_947ipeq.js +0 -1
- package/.next/static/chunks/02to42x11p557.js +0 -7
- package/.next/static/chunks/03~5pxwbxxw-b.js +0 -1
- package/.next/static/chunks/04~fkia6-79k3.js +0 -1
- package/.next/static/chunks/0561vp5-q5.zp.js +0 -1
- package/.next/static/chunks/05uk96gc~9mni.js +0 -1
- package/.next/static/chunks/085rejlait1fs.js +0 -1
- package/.next/static/chunks/0a.9~-nf0gpec.js +0 -1
- package/.next/static/chunks/0im751o4n61c7.js +0 -1
- package/.next/static/chunks/0inql3s9ldyx5.js +0 -1
- package/.next/static/chunks/0ku0fjqlm9mca.js +0 -1
- package/.next/static/chunks/0mcamnu4w_x1r.js +0 -4
- package/.next/static/chunks/0ss8u23bnbyry.js +0 -1
- package/.next/static/chunks/0~rv5y.y5my9s.css +0 -1
- package/.next/static/chunks/13yktdzuatx3d.js +0 -1
- package/.next/static/chunks/15-ltfhot3b4n.js +0 -7
- package/.next/static/chunks/16ls93seuyj8a.js +0 -1
- package/.next/static/chunks/17sxlwlx5fhrp.css +0 -1
- package/.next/static/chunks/181u38qblp8lz.js +0 -1
- 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/app/api/hub/license/route.ts +0 -33
- package/app/components/BroadcastBar.tsx +0 -84
- package/app/components/InboxPanel.tsx +0 -36
- /package/.next/static/{vv4Gz5yVhOzydMI2UlT1l → TDZA1Sx9EZPbGBOvEQtJ7}/_buildManifest.js +0 -0
- /package/.next/static/{vv4Gz5yVhOzydMI2UlT1l → TDZA1Sx9EZPbGBOvEQtJ7}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{vv4Gz5yVhOzydMI2UlT1l → TDZA1Sx9EZPbGBOvEQtJ7}/_ssgManifest.js +0 -0
package/app/admin/page.tsx
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useState } from 'react';
|
|
4
|
+
import Link from 'next/link';
|
|
4
5
|
import { useSessions, useHealth, useStats } from '../lib/hooks';
|
|
5
6
|
import { timeAgo } from '../components/utils';
|
|
6
7
|
import { AliasAvatar } from '../components/AliasAvatar';
|
|
7
|
-
import {
|
|
8
|
+
import { SESSION_STATUS_CHIP_CLASS } from '../lib/status';
|
|
8
9
|
|
|
9
10
|
export default function AdminPage() {
|
|
10
11
|
const { sessions } = useSessions();
|
|
@@ -73,7 +74,7 @@ export default function AdminPage() {
|
|
|
73
74
|
const offlineNodes = sessions.filter(s => !sseFor(s));
|
|
74
75
|
|
|
75
76
|
return (
|
|
76
|
-
<div className="min-h-screen bg-[#
|
|
77
|
+
<div className="min-h-screen bg-[#0b0b0d] text-gray-100 p-4 sm:p-6">
|
|
77
78
|
<h1 className="text-2xl font-bold text-white mb-3 lg:ml-0 ml-10">Admin</h1>
|
|
78
79
|
|
|
79
80
|
{/* Section anchor nav — same pattern as Settings r28; R2 of #190 mobile
|
|
@@ -82,14 +83,18 @@ export default function AdminPage() {
|
|
|
82
83
|
read as inert labels rather than tappable jumps. Wrapping each in a
|
|
83
84
|
visible bordered chip restores the affordance and lifts tap height
|
|
84
85
|
to ~44px without changing the desktop hierarchy. */}
|
|
85
|
-
|
|
86
|
+
{/* #209 R31 (mobile vertical rhythm — same pattern as R28/R30):
|
|
87
|
+
chip-jump nav was mb-8 (32 px) — biggest single gap on /admin
|
|
88
|
+
above the fold. Drop to mb-4 sm:mb-8 — phones get 16 px, sm:
|
|
89
|
+
up keeps the original breathing room. */}
|
|
90
|
+
<nav className="mb-4 sm:mb-8 flex flex-wrap gap-2 text-xs">
|
|
86
91
|
{[
|
|
87
92
|
{ href: '#status', label: 'Status' },
|
|
88
93
|
{ href: '#actions', label: 'Actions' },
|
|
89
94
|
{ href: '#users', label: 'Users' },
|
|
90
95
|
].map(a => (
|
|
91
96
|
<a key={a.href} href={a.href}
|
|
92
|
-
className="inline-flex min-h-[44px] items-center rounded-md border border-[#
|
|
97
|
+
className="inline-flex min-h-[44px] items-center rounded-md border border-[#26262b] bg-[#0e0e10]/60 px-3 py-2 text-gray-400 hover:border-cyan-500/50 hover:text-cyan-300 hover:bg-cyan-500/10 transition-colors">
|
|
93
98
|
{a.label}
|
|
94
99
|
</a>
|
|
95
100
|
))}
|
|
@@ -101,50 +106,60 @@ export default function AdminPage() {
|
|
|
101
106
|
<div id="status" className="space-y-4 scroll-mt-6">
|
|
102
107
|
<div className="flex items-center gap-2 px-1">
|
|
103
108
|
<div className="text-[11px] uppercase tracking-[0.14em] text-gray-400 font-semibold">Status</div>
|
|
104
|
-
<div className="flex-1 h-px bg-[#
|
|
109
|
+
<div className="flex-1 h-px bg-[#26262b]" />
|
|
105
110
|
</div>
|
|
106
111
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
|
107
112
|
|
|
108
|
-
{/* Server Overview
|
|
109
|
-
|
|
110
|
-
|
|
113
|
+
{/* Server Overview.
|
|
114
|
+
#209 R42: same mobile density pattern R39 brought to the
|
|
115
|
+
Overview StatsBar 4-card grid — p-5 → p-4 sm:p-5 and
|
|
116
|
+
text-3xl numbers → text-2xl sm:text-3xl on phones. The
|
|
117
|
+
2×2 numeric grid shrinks ~25 % on mobile (~60 px of fold
|
|
118
|
+
reclaimed) while reading identically on desktop. mb-4 on
|
|
119
|
+
the section heading also drops to mb-3 sm:mb-4. */}
|
|
120
|
+
<section className="bg-[#161618] border border-[#26262b] rounded-xl p-4 sm:p-5">
|
|
121
|
+
<h2 className="text-sm font-semibold text-gray-300 mb-3 sm:mb-4">Server Overview</h2>
|
|
111
122
|
<div className="grid grid-cols-2 gap-4 text-sm">
|
|
112
123
|
<div>
|
|
113
|
-
<div className="text-3xl font-bold text-green-400 tabular-nums">{onlineNodes.length}</div>
|
|
124
|
+
<div className="text-2xl sm:text-3xl font-bold text-green-400 tabular-nums">{onlineNodes.length}</div>
|
|
114
125
|
<div className="text-xs text-gray-500">Online</div>
|
|
115
126
|
</div>
|
|
116
127
|
<div>
|
|
117
|
-
<div className="text-3xl font-bold text-gray-500 tabular-nums">{offlineNodes.length}</div>
|
|
128
|
+
<div className="text-2xl sm:text-3xl font-bold text-gray-500 tabular-nums">{offlineNodes.length}</div>
|
|
118
129
|
<div className="text-xs text-gray-500">Offline</div>
|
|
119
130
|
</div>
|
|
120
131
|
<div>
|
|
121
|
-
<div className="text-3xl font-bold text-cyan-400 tabular-nums">{health?.sse_connections ?? '--'}</div>
|
|
132
|
+
<div className="text-2xl sm:text-3xl font-bold text-cyan-400 tabular-nums">{health?.sse_connections ?? '--'}</div>
|
|
122
133
|
<div className="text-xs text-gray-500">
|
|
123
134
|
SSE Streams <span className="text-gray-600">· server</span>
|
|
124
135
|
</div>
|
|
125
136
|
</div>
|
|
126
137
|
<div>
|
|
127
|
-
<div className="text-3xl font-bold text-white tabular-nums">{stats?.tasks?.total ?? '--'}</div>
|
|
138
|
+
<div className="text-2xl sm:text-3xl font-bold text-white tabular-nums">{stats?.tasks?.total ?? '--'}</div>
|
|
128
139
|
<div className="text-xs text-gray-500">Total Tasks</div>
|
|
129
140
|
</div>
|
|
130
141
|
</div>
|
|
131
|
-
{stats?.tasks?.by_status && stats.tasks.by_status.length > 0 && (
|
|
132
|
-
/*
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
142
|
+
{stats?.tasks?.by_status && stats.tasks.by_status.length > 0 && (() => {
|
|
143
|
+
/* #217 D7 (less is more): the 6-9 chip per-status wall
|
|
144
|
+
duplicated the /tasks tab strip. Same one-line summary
|
|
145
|
+
idiom as the Overview task line — running/failed are the
|
|
146
|
+
only numbers that drive action, the rest is on /tasks. */
|
|
147
|
+
const byStatus = Object.fromEntries(stats.tasks.by_status.map((s: { status: string; count: number }) => [s.status, s.count]));
|
|
148
|
+
return (
|
|
149
|
+
<div className="mt-4 flex items-center justify-between border-t border-[#26262b] pt-3 text-xs">
|
|
150
|
+
<div className="text-gray-400 tabular-nums">
|
|
151
|
+
<span className={byStatus['running'] ? 'text-green-400' : 'text-gray-500'}>{byStatus['running'] || 0} running</span>
|
|
152
|
+
<span className="text-gray-600"> · </span>
|
|
153
|
+
<span className={byStatus['failed'] ? 'text-red-400' : 'text-gray-500'}>{byStatus['failed'] || 0} failed</span>
|
|
154
|
+
</div>
|
|
155
|
+
<Link href="/tasks" prefetch={false} className="text-cyan-400 hover:text-cyan-300">View all →</Link>
|
|
156
|
+
</div>
|
|
157
|
+
);
|
|
158
|
+
})()}
|
|
144
159
|
</section>
|
|
145
160
|
|
|
146
161
|
{/* Online Sessions — pulled into Status group (was below Send Task) */}
|
|
147
|
-
<section className="bg-[#
|
|
162
|
+
<section className="bg-[#161618] border border-[#26262b] rounded-xl p-5">
|
|
148
163
|
<h2 className="text-sm font-semibold text-gray-300 mb-4">
|
|
149
164
|
Online Sessions <span className="text-gray-600">({onlineNodes.length})</span>
|
|
150
165
|
</h2>
|
|
@@ -152,7 +167,7 @@ export default function AdminPage() {
|
|
|
152
167
|
{onlineNodes.length === 0 ? (
|
|
153
168
|
<div className="text-xs text-gray-600 text-center py-4">No online sessions</div>
|
|
154
169
|
) : onlineNodes.map(s => (
|
|
155
|
-
<div key={s.alias} className="flex items-center gap-3 bg-[#
|
|
170
|
+
<div key={s.alias} className="flex items-center gap-3 bg-[#0e0e10] rounded-lg px-3 py-2 border border-[#1c1c1f]">
|
|
156
171
|
<AliasAvatar alias={s.alias} size={20} />
|
|
157
172
|
<div className="min-w-0 flex-1">
|
|
158
173
|
<div className="text-sm text-white font-medium truncate">{s.alias}</div>
|
|
@@ -177,28 +192,28 @@ export default function AdminPage() {
|
|
|
177
192
|
<div id="actions" className="space-y-4 scroll-mt-6">
|
|
178
193
|
<div className="flex items-center gap-2 px-1">
|
|
179
194
|
<div className="text-[11px] uppercase tracking-[0.14em] text-gray-400 font-semibold">Actions</div>
|
|
180
|
-
<div className="flex-1 h-px bg-[#
|
|
195
|
+
<div className="flex-1 h-px bg-[#26262b]" />
|
|
181
196
|
</div>
|
|
182
197
|
{sessions.length === 0 ? (
|
|
183
198
|
/* Round 77: Broadcast and Send Task both require at least one
|
|
184
199
|
registered agent. Surfacing the empty cards above an empty
|
|
185
200
|
fleet is the same dead-control class as the Overview
|
|
186
201
|
Dispatch button (r70). Replace with a single inline hint. */
|
|
187
|
-
<div className="bg-[#
|
|
202
|
+
<div className="bg-[#161618] border border-[#26262b] rounded-xl px-5 py-4 text-sm text-gray-500">
|
|
188
203
|
Broadcast and Send Task become available after the first agent registers.
|
|
189
204
|
</div>
|
|
190
205
|
) : (
|
|
191
206
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
|
192
207
|
|
|
193
208
|
{/* Broadcast */}
|
|
194
|
-
<section className="bg-[#
|
|
209
|
+
<section className="bg-[#161618] border border-[#26262b] rounded-xl p-5">
|
|
195
210
|
<h2 className="text-sm font-semibold text-gray-300 mb-4">Broadcast</h2>
|
|
196
211
|
<textarea
|
|
197
212
|
value={broadcastMsg}
|
|
198
213
|
onChange={e => setBroadcastMsg(e.target.value)}
|
|
199
214
|
placeholder="Message to all online nodes..."
|
|
200
215
|
rows={3}
|
|
201
|
-
className="w-full bg-[#
|
|
216
|
+
className="w-full bg-[#0e0e10] border border-[#26262b] rounded-lg px-3 py-2 text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:outline-none resize-none"
|
|
202
217
|
/>
|
|
203
218
|
<div className="flex justify-between items-center mt-3">
|
|
204
219
|
<span className="text-xs text-gray-600">{broadcastMsg.length}/500</span>
|
|
@@ -214,12 +229,12 @@ export default function AdminPage() {
|
|
|
214
229
|
</section>
|
|
215
230
|
|
|
216
231
|
{/* Send Task */}
|
|
217
|
-
<section className="bg-[#
|
|
232
|
+
<section className="bg-[#161618] border border-[#26262b] rounded-xl p-5">
|
|
218
233
|
<h2 className="text-sm font-semibold text-gray-300 mb-4">Send Task</h2>
|
|
219
234
|
<select
|
|
220
235
|
value={taskTarget}
|
|
221
236
|
onChange={e => setTaskTarget(e.target.value)}
|
|
222
|
-
className="w-full bg-[#
|
|
237
|
+
className="w-full bg-[#0e0e10] border border-[#26262b] rounded-lg px-3 py-2 text-base sm:text-sm text-white focus:border-cyan-500/50 focus:outline-none mb-3"
|
|
223
238
|
>
|
|
224
239
|
<option value="">Select target node...</option>
|
|
225
240
|
{onlineNodes.map(s => (
|
|
@@ -236,7 +251,7 @@ export default function AdminPage() {
|
|
|
236
251
|
onChange={e => setTaskContent(e.target.value)}
|
|
237
252
|
placeholder="Task content..."
|
|
238
253
|
rows={3}
|
|
239
|
-
className="w-full bg-[#
|
|
254
|
+
className="w-full bg-[#0e0e10] border border-[#26262b] rounded-lg px-3 py-2 text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:outline-none resize-none"
|
|
240
255
|
/>
|
|
241
256
|
<div className="flex justify-end mt-3">
|
|
242
257
|
<button
|
|
@@ -258,12 +273,12 @@ export default function AdminPage() {
|
|
|
258
273
|
<div id="users" className="space-y-4 scroll-mt-6">
|
|
259
274
|
<div className="flex items-center gap-2 px-1">
|
|
260
275
|
<div className="text-[11px] uppercase tracking-[0.14em] text-gray-400 font-semibold">Users</div>
|
|
261
|
-
<div className="flex-1 h-px bg-[#
|
|
276
|
+
<div className="flex-1 h-px bg-[#26262b]" />
|
|
262
277
|
</div>
|
|
263
278
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
|
264
279
|
|
|
265
280
|
{/* User Management (V3 Auth) */}
|
|
266
|
-
<section className="bg-[#
|
|
281
|
+
<section className="bg-[#161618] border border-[#26262b] rounded-xl p-5">
|
|
267
282
|
<h2 className="text-sm font-semibold text-gray-300 mb-4">Register User (V3)</h2>
|
|
268
283
|
<div className="space-y-3">
|
|
269
284
|
<input
|
|
@@ -271,14 +286,14 @@ export default function AdminPage() {
|
|
|
271
286
|
value={regUser}
|
|
272
287
|
onChange={e => setRegUser(e.target.value)}
|
|
273
288
|
placeholder="Username"
|
|
274
|
-
className="w-full bg-[#
|
|
289
|
+
className="w-full bg-[#0e0e10] border border-[#26262b] rounded-lg px-3 py-2 text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:outline-none"
|
|
275
290
|
/>
|
|
276
291
|
<input
|
|
277
292
|
type="password"
|
|
278
293
|
value={regPass}
|
|
279
294
|
onChange={e => setRegPass(e.target.value)}
|
|
280
295
|
placeholder="Password"
|
|
281
|
-
className="w-full bg-[#
|
|
296
|
+
className="w-full bg-[#0e0e10] border border-[#26262b] rounded-lg px-3 py-2 text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:outline-none"
|
|
282
297
|
/>
|
|
283
298
|
<button
|
|
284
299
|
onClick={registerUser}
|
|
@@ -28,10 +28,21 @@ export function AgentCard({ session: s, hasSse, sseCount, onChat }: AgentCardPro
|
|
|
28
28
|
<Link
|
|
29
29
|
href={`/node?alias=${encodeURIComponent(s.alias)}`}
|
|
30
30
|
prefetch={false}
|
|
31
|
+
onClick={e => {
|
|
32
|
+
// #217 M2 (Vincent tg 646 "为什么还要留一个 chat 按钮,直接点击
|
|
33
|
+
// 不就行了"): tapping a reachable agent's card opens chat
|
|
34
|
+
// directly — the Chat button is gone. Offline/unreachable cards
|
|
35
|
+
// keep navigating to /node detail, and detail for reachable
|
|
36
|
+
// agents stays available via the Agents tab.
|
|
37
|
+
if (onChat && hasSse) {
|
|
38
|
+
e.preventDefault();
|
|
39
|
+
onChat(s.alias);
|
|
40
|
+
}
|
|
41
|
+
}}
|
|
31
42
|
className={`anet-agent-card group relative block rounded-xl border p-3 sm:p-4 transition-all duration-300 cursor-pointer hover:-translate-y-0.5 ${
|
|
32
43
|
hasSse
|
|
33
|
-
? `bg-[#
|
|
34
|
-
: 'bg-[#
|
|
44
|
+
? `bg-[#161618] border-[#26262b] hover:border-cyan-500/30 hover:shadow-lg ${cfg.glow}`
|
|
45
|
+
: 'bg-[#111113] border-[#1c1c1f] opacity-40'
|
|
35
46
|
}`}
|
|
36
47
|
>
|
|
37
48
|
{/* Header: avatar + name + status. Avatar carries the alias→hue map
|
|
@@ -55,7 +66,7 @@ export function AgentCard({ session: s, hasSse, sseCount, onChat }: AgentCardPro
|
|
|
55
66
|
chews ~28px per card × 99 sessions on Overview mobile. The
|
|
56
67
|
agent type stays one tap away on /node detail. */}
|
|
57
68
|
<div className="hidden sm:flex items-center gap-2 mb-3">
|
|
58
|
-
<span className="text-xs text-gray-600 bg-[#
|
|
69
|
+
<span className="text-xs text-gray-600 bg-[#0e0e10] px-2 py-0.5 rounded border border-[#1c1c1f]">
|
|
59
70
|
{s.agent || 'unknown'}
|
|
60
71
|
</span>
|
|
61
72
|
{hasSse && (
|
|
@@ -67,12 +78,14 @@ export function AgentCard({ session: s, hasSse, sseCount, onChat }: AgentCardPro
|
|
|
67
78
|
single-line padding (px-2 py-1) so the task strip is ~28px
|
|
68
79
|
rather than ~56px. The full task is still in the title
|
|
69
80
|
tooltip and on /node detail. */}
|
|
70
|
-
{
|
|
71
|
-
|
|
81
|
+
{/* #217 M3: the "No active task" italic placeholder is gone — the
|
|
82
|
+
idle status chip in the header already says it, and the line
|
|
83
|
+
cost ~30px on every idle card across a 150-node fleet. The
|
|
84
|
+
task strip renders only when there is a task. */}
|
|
85
|
+
{s.task && (
|
|
86
|
+
<div className="text-xs text-gray-400 bg-[#0e0e10] rounded-lg px-2 sm:px-3 py-1 sm:py-2 border border-[#1c1c1f] line-clamp-1 sm:line-clamp-2" title={s.task}>
|
|
72
87
|
{s.task}
|
|
73
88
|
</div>
|
|
74
|
-
) : (
|
|
75
|
-
<div className="text-xs text-gray-700 italic">No active task</div>
|
|
76
89
|
)}
|
|
77
90
|
|
|
78
91
|
{/* Progress bar — hidden below sm so an empty 0% bar doesn't
|
|
@@ -96,22 +109,25 @@ export function AgentCard({ session: s, hasSse, sseCount, onChat }: AgentCardPro
|
|
|
96
109
|
{/* Footer: time + chat + hover chevron affordance (round 44).
|
|
97
110
|
The card is a <Link> so it's clickable everywhere, but with no
|
|
98
111
|
visible cue users may not realise. Chevron appears on hover and
|
|
99
|
-
slides right ~2px for a "drill in" hint.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
112
|
+
slides right ~2px for a "drill in" hint.
|
|
113
|
+
#209 R40: two small mobile tweaks —
|
|
114
|
+
(1) the hover chevron is `hidden sm:inline-block` because
|
|
115
|
+
touch devices never trigger the :hover state, and the
|
|
116
|
+
12 px wide reserved slot was just an empty void on
|
|
117
|
+
mobile — but flex with opacity-0 was still spending
|
|
118
|
+
12 + 8 (gap) = 20 px on the right side of every card.
|
|
119
|
+
(2) mt-3 → mt-2 sm:mt-3 mirrors the same density pattern
|
|
120
|
+
R28 / R39 brought to the rest of the page. */}
|
|
121
|
+
<div className="mt-2 sm:mt-3 flex justify-between items-center text-[10px] text-gray-600">
|
|
122
|
+
{/* #217 D5 (Vincent: 乱七八糟的元素都可以删掉): the raw server
|
|
123
|
+
hostname (cloud instance IDs like iZrj93…) is noise on a
|
|
124
|
+
phone card — desktop keeps it, /node detail always has it. */}
|
|
125
|
+
<span className="hidden sm:inline truncate" title={s.server || ''}>{s.server || '--'}</span>
|
|
126
|
+
<div className="flex items-center gap-2 ml-auto">
|
|
111
127
|
<span>{timeAgo(s.updated_at)}</span>
|
|
112
128
|
<svg
|
|
113
129
|
aria-hidden
|
|
114
|
-
className="w-3 h-3 text-gray-700 opacity-0 -translate-x-1 group-hover:opacity-100 group-hover:translate-x-0 transition-all duration-200"
|
|
130
|
+
className="hidden sm:inline-block w-3 h-3 text-gray-700 opacity-0 -translate-x-1 group-hover:opacity-100 group-hover:translate-x-0 transition-all duration-200"
|
|
115
131
|
fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"
|
|
116
132
|
>
|
|
117
133
|
<path d="M9 18l6-6-6-6" />
|
|
@@ -20,7 +20,13 @@ export function AppShell({ children }: { children: React.ReactNode }) {
|
|
|
20
20
|
return (
|
|
21
21
|
<div className="flex min-h-[100dvh] max-w-full overflow-x-hidden">
|
|
22
22
|
<Sidebar />
|
|
23
|
-
|
|
23
|
+
{/* R25 of #190: pair the existing bottom safe-area padding with a
|
|
24
|
+
top one so the HealthBanner clears the iOS status bar when
|
|
25
|
+
launched as an installed PWA (statusBarStyle: black-translucent
|
|
26
|
+
+ viewportFit: cover, see app/layout.tsx). On non-PWA contexts
|
|
27
|
+
env(safe-area-inset-top) is 0, so this is a no-op for browser
|
|
28
|
+
tab visits. */}
|
|
29
|
+
<main className="flex-1 min-w-0 max-w-full overflow-x-hidden flex flex-col pt-[env(safe-area-inset-top)]">
|
|
24
30
|
<HealthBanner />
|
|
25
31
|
<div className="flex-1 min-w-0 max-w-full overflow-x-hidden pb-[calc(5rem+env(safe-area-inset-bottom))] lg:pb-0">{children}</div>
|
|
26
32
|
</main>
|
|
@@ -191,7 +191,7 @@ export function ChatPopover({ alias, onClose }: ChatPopoverProps) {
|
|
|
191
191
|
// Chromium retargets the click to the header — the button never fires.
|
|
192
192
|
onPointerDown={(e) => e.stopPropagation()}
|
|
193
193
|
aria-label="Close chat"
|
|
194
|
-
className="text-[var(--fg-muted)] hover:text-[var(--fg)]
|
|
194
|
+
className="inline-flex min-h-[44px] min-w-[44px] items-center justify-center text-[var(--fg-muted)] hover:text-[var(--fg)] rounded-lg hover:bg-[var(--bg-elevated)] shrink-0"
|
|
195
195
|
>
|
|
196
196
|
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
197
197
|
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* CollapsibleSearch
|
|
5
|
+
* ──────────────────
|
|
6
|
+
* #209 R34 — Reusable WeChat-style search-toggle pattern.
|
|
7
|
+
*
|
|
8
|
+
* Two pieces of UI from one component:
|
|
9
|
+
* 1. A magnifier button (`w-9 h-9 rounded-full`) that lives in a page
|
|
10
|
+
* header — render it via `Button`.
|
|
11
|
+
* 2. A collapsible search row that reveals below the header on open —
|
|
12
|
+
* render it via `Row`.
|
|
13
|
+
*
|
|
14
|
+
* Used together they replicate WeChat's chat-list search affordance:
|
|
15
|
+
* the search field is hidden by default behind a small circle at the
|
|
16
|
+
* top-right of the page header. Tapping it slides the input open,
|
|
17
|
+
* autofocuses, and lets Escape clear + close. A cyan dot appears in
|
|
18
|
+
* the corner of the icon when there is an active search term but the
|
|
19
|
+
* row is collapsed, so the user always knows a filter is in effect.
|
|
20
|
+
*
|
|
21
|
+
* Owner of the search string is the parent (this component is
|
|
22
|
+
* uncontrolled w.r.t. value — pure UI). That keeps the filter logic
|
|
23
|
+
* with the page so it's easy to plumb additional behaviour like
|
|
24
|
+
* `from:alias` shortcuts or highlight rendering.
|
|
25
|
+
*
|
|
26
|
+
* Shipped as a single component with two named children so callers
|
|
27
|
+
* can place `<Button>` inside their existing header flex row and
|
|
28
|
+
* `<Row>` just after, without restructuring around a tighter API.
|
|
29
|
+
*
|
|
30
|
+
* First adopters: /nodes (R32), /messages (R33). R34 ships
|
|
31
|
+
* /nodes + /messages migrated to use this component; future pages
|
|
32
|
+
* can add search by importing it instead of copy-pasting the JSX.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
import { useState, useCallback } from 'react';
|
|
36
|
+
|
|
37
|
+
interface CollapsibleSearchAPI {
|
|
38
|
+
/** Round magnifier button. Place in your page header (e.g. right edge). */
|
|
39
|
+
Button: () => React.JSX.Element;
|
|
40
|
+
/** Collapsible search input row. Place immediately after the header. */
|
|
41
|
+
Row: () => React.JSX.Element | null;
|
|
42
|
+
/** Current search value — pass to your filter logic. */
|
|
43
|
+
value: string;
|
|
44
|
+
/** Programmatically clear + close (useful for "no results" reset link). */
|
|
45
|
+
reset: () => void;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface UseCollapsibleSearchProps {
|
|
49
|
+
/** Controlled value + setter from the parent — owns the search string. */
|
|
50
|
+
value: string;
|
|
51
|
+
onChange: (next: string) => void;
|
|
52
|
+
/** Input placeholder. */
|
|
53
|
+
placeholder?: string;
|
|
54
|
+
/** Accessible label for the button (also drives `title=`). */
|
|
55
|
+
label?: string;
|
|
56
|
+
/** Hide everything when false (e.g. when there is nothing to search). */
|
|
57
|
+
enabled?: boolean;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function useCollapsibleSearch({
|
|
61
|
+
value,
|
|
62
|
+
onChange,
|
|
63
|
+
placeholder = 'Search…',
|
|
64
|
+
label = 'Search',
|
|
65
|
+
enabled = true,
|
|
66
|
+
}: UseCollapsibleSearchProps): CollapsibleSearchAPI {
|
|
67
|
+
const [open, setOpen] = useState(false);
|
|
68
|
+
|
|
69
|
+
const reset = useCallback(() => {
|
|
70
|
+
onChange('');
|
|
71
|
+
setOpen(false);
|
|
72
|
+
}, [onChange]);
|
|
73
|
+
|
|
74
|
+
const Button = useCallback(() => {
|
|
75
|
+
if (!enabled) return <></>;
|
|
76
|
+
const active = open || value;
|
|
77
|
+
return (
|
|
78
|
+
<button
|
|
79
|
+
type="button"
|
|
80
|
+
onClick={() => setOpen(v => !v)}
|
|
81
|
+
aria-label={open ? `Close ${label.toLowerCase()}` : `Open ${label.toLowerCase()}`}
|
|
82
|
+
aria-pressed={open}
|
|
83
|
+
title={open ? `Close ${label.toLowerCase()}` : label}
|
|
84
|
+
className={`relative shrink-0 inline-flex items-center justify-center rounded-full border w-9 h-9 transition-colors ${
|
|
85
|
+
active
|
|
86
|
+
? 'border-cyan-500/40 bg-cyan-500/10 text-cyan-300'
|
|
87
|
+
: 'border-[#26262b] bg-[#161618] text-gray-400 hover:text-gray-200 hover:border-[#3a3a41]'
|
|
88
|
+
}`}
|
|
89
|
+
>
|
|
90
|
+
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
91
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
|
92
|
+
</svg>
|
|
93
|
+
{value && !open && (
|
|
94
|
+
<span aria-hidden className="absolute -top-0.5 -right-0.5 w-2 h-2 rounded-full bg-cyan-400 ring-2 ring-[#0b0b0d]" />
|
|
95
|
+
)}
|
|
96
|
+
</button>
|
|
97
|
+
);
|
|
98
|
+
}, [open, value, label, enabled]);
|
|
99
|
+
|
|
100
|
+
const Row = useCallback(() => {
|
|
101
|
+
if (!enabled) return null;
|
|
102
|
+
if (!open && !value) return null;
|
|
103
|
+
return (
|
|
104
|
+
<div className="mb-3 sm:mb-4 flex items-center gap-2">
|
|
105
|
+
<input
|
|
106
|
+
type="text"
|
|
107
|
+
value={value}
|
|
108
|
+
onChange={e => onChange(e.target.value)}
|
|
109
|
+
onKeyDown={e => { if (e.key === 'Escape') reset(); }}
|
|
110
|
+
placeholder={placeholder}
|
|
111
|
+
autoFocus={open}
|
|
112
|
+
className="flex-1 bg-[#161618] border border-[#26262b] rounded-lg px-3 py-2 text-base sm:text-sm text-white placeholder-gray-600 focus:border-cyan-500/40 focus:outline-none"
|
|
113
|
+
/>
|
|
114
|
+
<button
|
|
115
|
+
type="button"
|
|
116
|
+
onClick={reset}
|
|
117
|
+
className="shrink-0 text-xs text-gray-500 hover:text-gray-300 px-2 py-2"
|
|
118
|
+
aria-label="Clear and close search"
|
|
119
|
+
>
|
|
120
|
+
Cancel
|
|
121
|
+
</button>
|
|
122
|
+
</div>
|
|
123
|
+
);
|
|
124
|
+
}, [open, value, onChange, placeholder, reset, enabled]);
|
|
125
|
+
|
|
126
|
+
return { Button, Row, value, reset };
|
|
127
|
+
}
|
|
@@ -26,9 +26,9 @@ export function CommandCenter({ tabs, activeTab, onOpenTab, onCloseTab, onSetAct
|
|
|
26
26
|
<div className="fixed inset-0 bg-black/30 z-40 lg:hidden" onClick={onClose} />
|
|
27
27
|
|
|
28
28
|
{/* Panel */}
|
|
29
|
-
<div className="fixed top-0 right-0 h-[100dvh] w-full lg:w-[500px] bg-[#
|
|
29
|
+
<div className="fixed top-0 right-0 h-[100dvh] w-full lg:w-[500px] bg-[#0b0b0d] border-l border-[#26262b] z-50 flex flex-col shadow-2xl shadow-black/60 animate-slide-in">
|
|
30
30
|
{/* Tab bar */}
|
|
31
|
-
<div className="flex items-center border-b border-[#
|
|
31
|
+
<div className="flex items-center border-b border-[#26262b] bg-[#111113] overflow-x-auto">
|
|
32
32
|
<div className="flex-1 flex min-w-0">
|
|
33
33
|
{tabs.map(alias => (
|
|
34
34
|
<button
|
|
@@ -42,16 +42,26 @@ export function CommandCenter({ tabs, activeTab, onOpenTab, onCloseTab, onSetAct
|
|
|
42
42
|
>
|
|
43
43
|
<div className={`w-2 h-2 rounded-full ${activeTab === alias ? 'bg-cyan-400' : 'bg-gray-600'}`} />
|
|
44
44
|
<span className="max-w-[80px] truncate">{alias}</span>
|
|
45
|
+
{/* R21 of #190 mobile a11y: nested close had no aria-label
|
|
46
|
+
(screen-readers spoke just "button") and an ~8 px
|
|
47
|
+
tap target (p-0.5 + text "×"). Both fixed; nested
|
|
48
|
+
<button> inside <button> remains invalid HTML but
|
|
49
|
+
is browser-tolerated and refactoring to role="tab"
|
|
50
|
+
is out of scope for this round. */}
|
|
45
51
|
<button
|
|
46
52
|
onClick={e => { e.stopPropagation(); onCloseTab(alias); }}
|
|
47
|
-
|
|
53
|
+
aria-label={`Close ${alias} chat tab`}
|
|
54
|
+
className="ml-1 inline-flex h-7 w-7 items-center justify-center text-gray-600 hover:text-gray-300 rounded-md hover:bg-white/5"
|
|
48
55
|
>
|
|
49
56
|
×
|
|
50
57
|
</button>
|
|
51
58
|
</button>
|
|
52
59
|
))}
|
|
53
60
|
</div>
|
|
54
|
-
|
|
61
|
+
{/* R21 mobile a11y: outer command-center close was SVG-only
|
|
62
|
+
and screen-reader silent; also bumped to 44 x 44 hit zone
|
|
63
|
+
from 32 x ~36. */}
|
|
64
|
+
<button onClick={onClose} aria-label="Close command center" className="inline-flex min-h-[44px] min-w-[44px] items-center justify-center text-gray-500 hover:text-white shrink-0 border-l border-[#26262b]">
|
|
55
65
|
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
56
66
|
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
|
|
57
67
|
</svg>
|
|
@@ -426,10 +426,10 @@ export function CommandPalette() {
|
|
|
426
426
|
>
|
|
427
427
|
<div className="absolute inset-0 bg-black/50 backdrop-blur-sm" aria-hidden />
|
|
428
428
|
<div
|
|
429
|
-
className="relative w-full max-w-xl rounded-xl border border-[#
|
|
429
|
+
className="relative w-full max-w-xl rounded-xl border border-[#26262b] bg-[#111113] shadow-2xl shadow-black/40 overflow-hidden"
|
|
430
430
|
onClick={e => e.stopPropagation()}
|
|
431
431
|
>
|
|
432
|
-
<div className="flex items-center gap-2 border-b border-[#
|
|
432
|
+
<div className="flex items-center gap-2 border-b border-[#26262b] px-3 py-2.5">
|
|
433
433
|
<svg width="14" height="14" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="1.5" className="text-gray-500 shrink-0">
|
|
434
434
|
<path strokeLinecap="round" strokeLinejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
|
435
435
|
</svg>
|
|
@@ -438,9 +438,9 @@ export function CommandPalette() {
|
|
|
438
438
|
value={query}
|
|
439
439
|
onChange={e => { setQuery(e.target.value); setSelected(0); }}
|
|
440
440
|
placeholder="Type a command or search…"
|
|
441
|
-
className="flex-1 bg-transparent text-sm text-gray-200 placeholder-gray-600 focus:outline-none"
|
|
441
|
+
className="flex-1 bg-transparent text-base sm:text-sm text-gray-200 placeholder-gray-600 focus:outline-none"
|
|
442
442
|
/>
|
|
443
|
-
<kbd className="text-[10px] text-gray-600 border border-[#
|
|
443
|
+
<kbd className="text-[10px] text-gray-600 border border-[#26262b] rounded px-1.5 py-0.5 font-mono">esc</kbd>
|
|
444
444
|
</div>
|
|
445
445
|
|
|
446
446
|
<div className="max-h-[60vh] overflow-y-auto py-2">
|
|
@@ -461,7 +461,7 @@ export function CommandPalette() {
|
|
|
461
461
|
onMouseEnter={() => setSelected(idx)}
|
|
462
462
|
onClick={() => { pushRecent(c.id); c.perform(router); setOpen(false); }}
|
|
463
463
|
className={`anet-cmdk-row w-full flex items-center gap-3 px-3 py-2 text-left text-sm ${
|
|
464
|
-
isActive ? 'bg-cyan-500/10 text-cyan-300' : 'text-gray-300 hover:bg-[#
|
|
464
|
+
isActive ? 'bg-cyan-500/10 text-cyan-300' : 'text-gray-300 hover:bg-[#1c1c1f]/50'
|
|
465
465
|
}`}
|
|
466
466
|
>
|
|
467
467
|
<span className={isActive ? 'text-cyan-400' : 'text-gray-500'}>{c.icon}</span>
|
|
@@ -484,7 +484,7 @@ export function CommandPalette() {
|
|
|
484
484
|
)}
|
|
485
485
|
</div>
|
|
486
486
|
|
|
487
|
-
<div className="border-t border-[#
|
|
487
|
+
<div className="border-t border-[#26262b] px-3 py-1.5 flex items-center justify-between text-[10px] text-gray-600">
|
|
488
488
|
<span className="flex items-center gap-3">
|
|
489
489
|
<span><kbd className="font-mono">↑↓</kbd> navigate</span>
|
|
490
490
|
<span><kbd className="font-mono">↵</kbd> select</span>
|
|
@@ -73,14 +73,17 @@ export function DispatchPanel({ sessions, onClose }: DispatchPanelProps) {
|
|
|
73
73
|
return (
|
|
74
74
|
<>
|
|
75
75
|
<div className="fixed inset-0 bg-black/50 z-40 anet-fade-in" onClick={onClose} />
|
|
76
|
-
<div className="fixed inset-x-3 inset-y-3 max-h-[calc(100dvh-1.5rem)] lg:inset-x-[15%] lg:inset-y-[5%] lg:max-h-none bg-[#
|
|
76
|
+
<div className="fixed inset-x-3 inset-y-3 max-h-[calc(100dvh-1.5rem)] lg:inset-x-[15%] lg:inset-y-[5%] lg:max-h-none bg-[#0b0b0d] border border-[#26262b] rounded-2xl z-50 flex flex-col shadow-2xl shadow-black/70 overflow-hidden anet-fade-in">
|
|
77
77
|
{/* Header */}
|
|
78
|
-
<div className="flex items-center justify-between px-6 py-4 border-b border-[#
|
|
78
|
+
<div className="flex items-center justify-between px-6 py-4 border-b border-[#26262b] bg-[#111113]">
|
|
79
79
|
<div>
|
|
80
80
|
<h2 className="text-lg font-bold text-white">Dispatch Task</h2>
|
|
81
81
|
<p className="text-xs text-gray-500 mt-0.5">Send a task to one or more agents</p>
|
|
82
82
|
</div>
|
|
83
|
-
|
|
83
|
+
{/* R22 of #190: same SVG-only-no-aria-label pattern that R16
|
|
84
|
+
fixed on TaskChatPanel/TaskDrawer/ChatPopover; DispatchPanel
|
|
85
|
+
missed that pass. ~32 px hit zone + silent on screen-reader. */}
|
|
86
|
+
<button onClick={onClose} aria-label="Close dispatch panel" className="inline-flex min-h-[44px] min-w-[44px] items-center justify-center text-gray-500 hover:text-white rounded-lg hover:bg-[#1c1c1f]">
|
|
84
87
|
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
85
88
|
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
|
|
86
89
|
</svg>
|
|
@@ -89,12 +92,12 @@ export function DispatchPanel({ sessions, onClose }: DispatchPanelProps) {
|
|
|
89
92
|
|
|
90
93
|
<div className="flex-1 flex flex-col lg:flex-row overflow-hidden">
|
|
91
94
|
{/* Left: Node selection */}
|
|
92
|
-
<div className="lg:w-[280px] border-b lg:border-b-0 lg:border-r border-[#
|
|
93
|
-
<div className="px-4 py-3 border-b border-[#
|
|
95
|
+
<div className="lg:w-[280px] border-b lg:border-b-0 lg:border-r border-[#26262b] flex flex-col">
|
|
96
|
+
<div className="px-4 py-3 border-b border-[#26262b]">
|
|
94
97
|
<input
|
|
95
98
|
type="text" value={filter} onChange={e => setFilter(e.target.value)}
|
|
96
99
|
placeholder="Filter agents..."
|
|
97
|
-
className="w-full bg-[#
|
|
100
|
+
className="w-full bg-[#161618] border border-[#26262b] rounded-lg px-3 py-2 text-base sm:text-xs text-white placeholder-gray-600 focus:border-cyan-500/40 focus:outline-none"
|
|
98
101
|
/>
|
|
99
102
|
<div className="flex items-center justify-between mt-2">
|
|
100
103
|
<button onClick={selectAll} className="text-[10px] text-cyan-400 hover:text-cyan-300">
|
|
@@ -107,7 +110,7 @@ export function DispatchPanel({ sessions, onClose }: DispatchPanelProps) {
|
|
|
107
110
|
{filtered.map(s => (
|
|
108
111
|
<button key={s.alias} onClick={() => toggleNode(s.alias)}
|
|
109
112
|
className={`w-full flex items-center gap-2 px-3 py-2 rounded-lg text-xs text-left transition-colors ${
|
|
110
|
-
selected.has(s.alias) ? 'bg-cyan-500/10 text-cyan-300 border border-cyan-500/20' : 'text-gray-400 hover:bg-[#
|
|
113
|
+
selected.has(s.alias) ? 'bg-cyan-500/10 text-cyan-300 border border-cyan-500/20' : 'text-gray-400 hover:bg-[#1c1c1f]'
|
|
111
114
|
}`}>
|
|
112
115
|
<AliasAvatar alias={s.alias} size={16} />
|
|
113
116
|
<div className={`w-1.5 h-1.5 rounded-full shrink-0 ${s.status === 'working' ? 'bg-green-400' : s.status === 'idle' ? 'bg-cyan-400' : 'bg-gray-500'}`} />
|
|
@@ -126,12 +129,12 @@ export function DispatchPanel({ sessions, onClose }: DispatchPanelProps) {
|
|
|
126
129
|
<textarea
|
|
127
130
|
value={prompt} onChange={e => setPrompt(e.target.value)}
|
|
128
131
|
placeholder="Enter the task you want to dispatch..."
|
|
129
|
-
className="flex-1 min-h-[120px] bg-[#
|
|
132
|
+
className="flex-1 min-h-[120px] bg-[#161618] border border-[#26262b] rounded-xl px-4 py-3 text-base sm:text-sm text-white placeholder-gray-600 focus:border-cyan-500/40 focus:outline-none resize-none"
|
|
130
133
|
/>
|
|
131
134
|
|
|
132
135
|
<div className="flex items-center gap-3 mt-4">
|
|
133
136
|
<select value={priority} onChange={e => setPriority(e.target.value)}
|
|
134
|
-
className="bg-[#
|
|
137
|
+
className="bg-[#161618] border border-[#26262b] rounded-lg px-3 py-2 text-base sm:text-xs text-white focus:outline-none">
|
|
135
138
|
<option value="normal">Normal priority</option>
|
|
136
139
|
<option value="high">High priority</option>
|
|
137
140
|
<option value="low">Low priority</option>
|
|
@@ -155,7 +158,7 @@ export function DispatchPanel({ sessions, onClose }: DispatchPanelProps) {
|
|
|
155
158
|
|
|
156
159
|
{/* Results */}
|
|
157
160
|
{results.length > 0 && (
|
|
158
|
-
<div className="px-6 py-3 border-t border-[#
|
|
161
|
+
<div className="px-6 py-3 border-t border-[#26262b] bg-[#111113]">
|
|
159
162
|
<div className="text-xs text-gray-500 mb-2">
|
|
160
163
|
{successCount}/{results.length} dispatched successfully
|
|
161
164
|
</div>
|