@sleep2agi/agent-network-dashboard 0.5.7-preview.4 → 0.5.7-preview.40
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/cache/.previewinfo +1 -0
- package/.next/cache/.rscinfo +1 -0
- package/.next/diagnostics/route-bundle-stats.json +51 -51
- 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.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 +3 -3
- package/.next/server/app/_not-found.rsc +15 -15
- package/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
- package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/server/app/_not-found.segments/_index.segment.rsc +8 -8
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/admin/page.js.nft.json +1 -1
- package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
- package/.next/server/app/admin.html +3 -3
- package/.next/server/app/admin.rsc +17 -17
- package/.next/server/app/admin.segments/_full.segment.rsc +17 -17
- package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
- package/.next/server/app/admin.segments/_index.segment.rsc +8 -8
- package/.next/server/app/admin.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/admin.segments/admin.segment.rsc +3 -3
- package/.next/server/app/index.html +3 -3
- package/.next/server/app/index.rsc +17 -17
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/index.segments/_full.segment.rsc +17 -17
- package/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/server/app/index.segments/_index.segment.rsc +8 -8
- package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login/page.js.nft.json +1 -1
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/login.html +2 -2
- package/.next/server/app/login.rsc +17 -17
- package/.next/server/app/login.segments/_full.segment.rsc +17 -17
- package/.next/server/app/login.segments/_head.segment.rsc +4 -4
- package/.next/server/app/login.segments/_index.segment.rsc +8 -8
- package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/login.segments/login.segment.rsc +3 -3
- package/.next/server/app/logs/page.js.nft.json +1 -1
- package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/logs.html +3 -3
- package/.next/server/app/logs.rsc +17 -17
- package/.next/server/app/logs.segments/_full.segment.rsc +17 -17
- package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
- package/.next/server/app/logs.segments/_index.segment.rsc +8 -8
- package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
- package/.next/server/app/messages/page.js.nft.json +1 -1
- package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
- package/.next/server/app/messages.html +3 -3
- package/.next/server/app/messages.rsc +17 -17
- package/.next/server/app/messages.segments/_full.segment.rsc +17 -17
- package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
- package/.next/server/app/messages.segments/_index.segment.rsc +8 -8
- package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
- package/.next/server/app/node/page.js.nft.json +1 -1
- package/.next/server/app/node/page_client-reference-manifest.js +1 -1
- package/.next/server/app/node.html +3 -3
- package/.next/server/app/node.rsc +17 -17
- package/.next/server/app/node.segments/_full.segment.rsc +17 -17
- package/.next/server/app/node.segments/_head.segment.rsc +4 -4
- package/.next/server/app/node.segments/_index.segment.rsc +8 -8
- package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/node.segments/node.segment.rsc +3 -3
- package/.next/server/app/nodes/page.js.nft.json +1 -1
- package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
- package/.next/server/app/nodes.html +3 -3
- package/.next/server/app/nodes.rsc +17 -17
- package/.next/server/app/nodes.segments/_full.segment.rsc +17 -17
- package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
- package/.next/server/app/nodes.segments/_index.segment.rsc +8 -8
- package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
- package/.next/server/app/page.js.nft.json +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/server-logs/page.js.nft.json +1 -1
- package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/server-logs.html +3 -3
- package/.next/server/app/server-logs.rsc +17 -17
- package/.next/server/app/server-logs.segments/_full.segment.rsc +17 -17
- package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
- package/.next/server/app/server-logs.segments/_index.segment.rsc +8 -8
- package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
- package/.next/server/app/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 +3 -3
- package/.next/server/app/servers.rsc +17 -17
- package/.next/server/app/servers.segments/_full.segment.rsc +17 -17
- package/.next/server/app/servers.segments/_head.segment.rsc +4 -4
- package/.next/server/app/servers.segments/_index.segment.rsc +8 -8
- package/.next/server/app/servers.segments/_tree.segment.rsc +2 -2
- 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.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 +3 -3
- package/.next/server/app/settings/networks.rsc +17 -17
- package/.next/server/app/settings/networks.segments/_full.segment.rsc +17 -17
- package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings/networks.segments/_index.segment.rsc +8 -8
- package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
- package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
- package/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/tokens/page.js.nft.json +1 -1
- package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/tokens.html +3 -3
- package/.next/server/app/settings/tokens.rsc +17 -17
- package/.next/server/app/settings/tokens.segments/_full.segment.rsc +17 -17
- package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings/tokens.segments/_index.segment.rsc +8 -8
- package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
- package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
- package/.next/server/app/settings.html +3 -3
- package/.next/server/app/settings.rsc +17 -17
- package/.next/server/app/settings.segments/_full.segment.rsc +17 -17
- package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings.segments/_index.segment.rsc +8 -8
- package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
- package/.next/server/app/tasks/[id]/page.js.nft.json +1 -1
- package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/tasks/page.js.nft.json +1 -1
- package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
- package/.next/server/app/tasks.html +3 -3
- package/.next/server/app/tasks.rsc +17 -17
- package/.next/server/app/tasks.segments/_full.segment.rsc +17 -17
- package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
- package/.next/server/app/tasks.segments/_index.segment.rsc +8 -8
- package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
- package/.next/server/app-paths-manifest.json +0 -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]__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]__0lu1wok._.js +2 -2
- package/.next/server/chunks/ssr/[root-of-the-server]__0lu1wok._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0torghd._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0torghd._.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_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_0i3759l._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js.map +1 -1
- 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_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/pages/404.html +3 -3
- 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/{181u38qblp8lz.js → 00b-ysl~m~dr~.js} +1 -1
- package/.next/static/chunks/{0jp~cs9-zkmqa.js → 00b4y77vxfabl.js} +1 -1
- package/.next/static/chunks/00d~isei247an.js +1 -0
- package/.next/static/chunks/01b8xnbwme3q3.js +1 -0
- package/.next/static/chunks/{15qxef.ilfysw.js → 02i~-uz0v~7ke.js} +3 -3
- package/.next/static/chunks/03rfxivuawnc8.css +1 -0
- package/.next/static/chunks/{0a.9~-nf0gpec.js → 0_eddxjio~tei.js} +1 -1
- package/.next/static/chunks/0_m0zx8f~zx54.js +7 -0
- package/.next/static/chunks/0bl.smy4d4mgc.js +1 -0
- package/.next/static/chunks/{0im751o4n61c7.js → 0hv6izw.g6cnm.js} +1 -1
- package/.next/static/chunks/0miet1u_5tj06.js +7 -0
- package/.next/static/chunks/0q~ko.knz4cq8.js +1 -0
- package/.next/static/chunks/{05uk96gc~9mni.js → 0ti3v67ixu43p.js} +1 -1
- package/.next/static/chunks/0urrkhru4u8d2.js +1 -0
- package/.next/static/chunks/{0wtp_nw--hl5t.js → 0~ex__fs7w2f1.js} +1 -1
- package/.next/static/chunks/161etkmg~z~-d.js +1 -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 +6 -2
- package/app/components/AgentCard.tsx +19 -9
- 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 +12 -2
- package/app/components/CommandPalette.tsx +1 -1
- package/app/components/DispatchPanel.tsx +7 -4
- package/app/components/HealthBanner.tsx +10 -2
- package/app/components/HelpOverlay.tsx +0 -3
- package/app/components/MobileNav.tsx +26 -19
- package/app/components/Sidebar.tsx +19 -12
- package/app/components/StatsBar.tsx +8 -1
- package/app/components/TaskChatPanel.tsx +19 -5
- package/app/components/TaskDrawer.tsx +3 -1
- package/app/components/ThemeSwitcher.tsx +10 -79
- package/app/components/TopoGraph.tsx +11 -1
- package/app/components/UserBar.tsx +3 -3
- package/app/globals.css +76 -707
- package/app/layout.tsx +27 -1
- package/app/lib/hooks.ts +0 -5
- package/app/login/page.tsx +3 -3
- package/app/logs/page.tsx +6 -2
- package/app/messages/page.tsx +72 -48
- package/app/node/page.tsx +2 -2
- package/app/nodes/page.tsx +52 -22
- package/app/page.tsx +12 -29
- package/app/server-logs/page.tsx +39 -11
- package/app/settings/networks/page.tsx +3 -3
- package/app/settings/page.tsx +25 -80
- package/app/settings/tokens/page.tsx +1 -1
- package/app/tasks/page.tsx +36 -14
- package/package.json +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]__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/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.66f3.rtcybb.js +0 -1
- package/.next/static/chunks/03~5pxwbxxw-b.js +0 -1
- package/.next/static/chunks/0561vp5-q5.zp.js +0 -1
- package/.next/static/chunks/09p_4a62~yi52.js +0 -1
- package/.next/static/chunks/0cjz0r1v8sx_f.js +0 -1
- package/.next/static/chunks/0h_gdeiy~s92j.css +0 -1
- package/.next/static/chunks/0inql3s9ldyx5.js +0 -1
- package/.next/static/chunks/0nqm.7w9_inwd.js +0 -7
- package/.next/static/chunks/15-ltfhot3b4n.js +0 -7
- 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/{77VfHxRKplwAYs09P-QWj → CaCQnZUQ_aajX60atUSzY}/_buildManifest.js +0 -0
- /package/.next/static/{77VfHxRKplwAYs09P-QWj → CaCQnZUQ_aajX60atUSzY}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{77VfHxRKplwAYs09P-QWj → CaCQnZUQ_aajX60atUSzY}/_ssgManifest.js +0 -0
package/app/layout.tsx
CHANGED
|
@@ -20,7 +20,26 @@ export const metadata: Metadata = {
|
|
|
20
20
|
statusBarStyle: "black-translucent",
|
|
21
21
|
title: "Agent Network",
|
|
22
22
|
},
|
|
23
|
-
|
|
23
|
+
// R26 of #190: Next.js 16 renders `mobile-web-app-capable` (the modern
|
|
24
|
+
// standardized meta) but NOT the legacy `apple-mobile-web-app-capable`.
|
|
25
|
+
// iOS Safari < 16.4 only checks the apple-prefixed name; without it,
|
|
26
|
+
// the dashboard won't install as a web app on those iOS versions and
|
|
27
|
+
// the statusBarStyle + apple-touch-icon (R24) are silently ignored.
|
|
28
|
+
// Emit the legacy alias via metadata.other for back-compat.
|
|
29
|
+
other: {
|
|
30
|
+
'apple-mobile-web-app-capable': 'yes',
|
|
31
|
+
},
|
|
32
|
+
// R24 of #190: appleWebApp.capable above declares this is an iOS web
|
|
33
|
+
// app, but only `icon` was specified, leaving iOS Safari to fall back
|
|
34
|
+
// to a generic icon on Add-to-Home-Screen. iOS 15+ accepts SVG via
|
|
35
|
+
// `rel="apple-touch-icon" type="image/svg+xml"`; older iOS will fall
|
|
36
|
+
// back to the manifest icons (added R23) and then the generic. Reuse
|
|
37
|
+
// the existing sleep2agi-logo.svg — no new asset shipped.
|
|
38
|
+
icons: {
|
|
39
|
+
icon: [{ url: '/favicon.svg', type: 'image/svg+xml' }],
|
|
40
|
+
apple: [{ url: '/sleep2agi-logo.svg', type: 'image/svg+xml' }],
|
|
41
|
+
shortcut: '/favicon.svg',
|
|
42
|
+
},
|
|
24
43
|
openGraph: {
|
|
25
44
|
title: "Agent Network Dashboard",
|
|
26
45
|
description: "Real-time monitoring dashboard for Agent Network nodes",
|
|
@@ -34,6 +53,13 @@ export const viewport: Viewport = {
|
|
|
34
53
|
maximumScale: 5,
|
|
35
54
|
userScalable: true,
|
|
36
55
|
themeColor: "#0a0a1a",
|
|
56
|
+
// R25 of #190: metadata.appleWebApp.statusBarStyle above is
|
|
57
|
+
// "black-translucent", which renders the iOS status bar as a
|
|
58
|
+
// transparent overlay above the page (rather than reserving its own
|
|
59
|
+
// strip). Without viewportFit: "cover" the resulting safe-area-inset
|
|
60
|
+
// env() values resolve to 0, so the HealthBanner gets occluded by
|
|
61
|
+
// the clock/battery row when the user opens the installed PWA.
|
|
62
|
+
viewportFit: "cover",
|
|
37
63
|
};
|
|
38
64
|
|
|
39
65
|
// Inline pre-paint script to apply persisted theme before React hydrates,
|
package/app/lib/hooks.ts
CHANGED
|
@@ -63,11 +63,6 @@ export function useStats() {
|
|
|
63
63
|
return { stats: data?.ok ? data : null, error };
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
export function useLicense() {
|
|
67
|
-
const { data, error } = useSWR('/api/hub/license', fetcher, { refreshInterval: 60000 });
|
|
68
|
-
return { license: data?.ok ? data : null, error };
|
|
69
|
-
}
|
|
70
|
-
|
|
71
66
|
export function useMessages(limit = 100) {
|
|
72
67
|
const { networkId } = useNetworkId();
|
|
73
68
|
const { data, error, isLoading } = useSWR(
|
package/app/login/page.tsx
CHANGED
|
@@ -92,7 +92,7 @@ export default function LoginPage() {
|
|
|
92
92
|
</label>
|
|
93
93
|
<input id="username" type="text" value={username} onChange={e => setUsername(e.target.value)} autoFocus
|
|
94
94
|
placeholder="Enter username"
|
|
95
|
-
className="w-full bg-[#0a0a15] border border-[#2a2a4a] rounded-lg px-4 py-3 text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:ring-1 focus:ring-cyan-500/20 focus:outline-none transition-all mb-4" />
|
|
95
|
+
className="w-full bg-[#0a0a15] border border-[#2a2a4a] rounded-lg px-4 py-3 text-base sm:text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:ring-1 focus:ring-cyan-500/20 focus:outline-none transition-all mb-4" />
|
|
96
96
|
|
|
97
97
|
<label htmlFor="password" className="block text-xs text-gray-500 mb-2 uppercase tracking-wider">
|
|
98
98
|
Password
|
|
@@ -100,13 +100,13 @@ export default function LoginPage() {
|
|
|
100
100
|
<div className="relative">
|
|
101
101
|
<input id="password" type={showPassword ? 'text' : 'password'} value={password} onChange={e => setPassword(e.target.value)}
|
|
102
102
|
placeholder="Enter password"
|
|
103
|
-
className="w-full bg-[#0a0a15] border border-[#2a2a4a] rounded-lg px-4 pr-11 py-3 text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:ring-1 focus:ring-cyan-500/20 focus:outline-none transition-all" />
|
|
103
|
+
className="w-full bg-[#0a0a15] border border-[#2a2a4a] rounded-lg px-4 pr-11 py-3 text-base sm:text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:ring-1 focus:ring-cyan-500/20 focus:outline-none transition-all" />
|
|
104
104
|
<button
|
|
105
105
|
type="button"
|
|
106
106
|
onClick={() => setShowPassword(s => !s)}
|
|
107
107
|
tabIndex={-1}
|
|
108
108
|
aria-label={showPassword ? 'Hide password' : 'Show password'}
|
|
109
|
-
className="absolute inset-y-0 right-0 flex items-center px-3 text-gray-600 hover:text-gray-300 transition-colors"
|
|
109
|
+
className="absolute inset-y-0 right-0 flex items-center justify-center min-w-[44px] px-3 text-gray-600 hover:text-gray-300 transition-colors"
|
|
110
110
|
>
|
|
111
111
|
{showPassword ? (
|
|
112
112
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
|
package/app/logs/page.tsx
CHANGED
|
@@ -162,11 +162,15 @@ export default function LogsPage() {
|
|
|
162
162
|
) : logs.length === 0 ? (
|
|
163
163
|
<EmptyState variant="logs" />
|
|
164
164
|
) : (
|
|
165
|
-
|
|
165
|
+
/* R18 of #190 mobile polish: /logs was 28,791 px on 390 px
|
|
166
|
+
mobile; same dense-card pattern that R7/R8 hit on /nodes
|
|
167
|
+
and /tasks. Tighten card padding and inter-card gap on
|
|
168
|
+
mobile, sm:-gated so desktop stays comfortable. */
|
|
169
|
+
<div className="space-y-1 sm:space-y-2">
|
|
166
170
|
{logs.map(log => {
|
|
167
171
|
const userName = log.username || log.user_id;
|
|
168
172
|
return (
|
|
169
|
-
<div key={log.id} className="relative bg-[#111128] border border-[#2a2a4a] rounded-lg pl-4 pr-4 py-3 hover:border-[#3a3a5a] transition-colors overflow-hidden">
|
|
173
|
+
<div key={log.id} className="relative bg-[#111128] border border-[#2a2a4a] rounded-lg pl-3 pr-3 py-2 sm:pl-4 sm:pr-4 sm:py-3 hover:border-[#3a3a5a] transition-colors overflow-hidden">
|
|
170
174
|
{/* 2px left rail per action (round 33) — failed logins, token
|
|
171
175
|
rotations spike out of a wall of register/login rows. */}
|
|
172
176
|
<span
|
package/app/messages/page.tsx
CHANGED
|
@@ -5,6 +5,7 @@ import { useMessages } from '../lib/hooks';
|
|
|
5
5
|
import { timeAgo, previewContent } from '../components/utils';
|
|
6
6
|
import { EmptyState } from '../components/EmptyState';
|
|
7
7
|
import { AliasAvatar } from '../components/AliasAvatar';
|
|
8
|
+
import { useCollapsibleSearch } from '../components/CollapsibleSearch';
|
|
8
9
|
|
|
9
10
|
interface MessageItem {
|
|
10
11
|
id: string;
|
|
@@ -58,6 +59,14 @@ export default function MessagesPage() {
|
|
|
58
59
|
const [search, setSearch] = useState('');
|
|
59
60
|
const [debug, setDebug] = useState(false);
|
|
60
61
|
const [viewMode, setViewMode] = useState<'timeline' | 'grouped'>('timeline');
|
|
62
|
+
// #209 R33→R34: WeChat-style magnifier-toggle search hook (shared with /nodes).
|
|
63
|
+
const searchCtl = useCollapsibleSearch({
|
|
64
|
+
value: search,
|
|
65
|
+
onChange: setSearch,
|
|
66
|
+
placeholder: 'Search from/to/content or use from:alias…',
|
|
67
|
+
label: 'Search messages',
|
|
68
|
+
enabled: messages.length > 0,
|
|
69
|
+
});
|
|
61
70
|
|
|
62
71
|
const quickFromChips = useMemo(() => {
|
|
63
72
|
const aliases = new Set<string>();
|
|
@@ -105,55 +114,63 @@ export default function MessagesPage() {
|
|
|
105
114
|
|
|
106
115
|
return (
|
|
107
116
|
<div className="min-h-screen bg-[#0a0a1a] text-gray-100 p-4 sm:p-6 font-mono">
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
<
|
|
116
|
-
className="text-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
<
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
`**${m.from_alias || '?'}** → ${m.to_alias || '?'} (${m.type || '?'}) — ${m.created_at || ''}\n\n${m.content || '--'}\n\n---`
|
|
126
|
-
).join('\n\n');
|
|
127
|
-
const blob = new Blob([`# Messages Export\n\n${md}`], { type: 'text/markdown' });
|
|
128
|
-
const url = URL.createObjectURL(blob);
|
|
129
|
-
const a = document.createElement('a');
|
|
130
|
-
a.href = url; a.download = `messages-${new Date().toISOString().slice(0,10)}.md`;
|
|
131
|
-
a.click(); URL.revokeObjectURL(url);
|
|
132
|
-
}}
|
|
133
|
-
className="text-xs text-gray-500 hover:text-gray-300 border border-gray-700/50 px-2.5 py-1 rounded-lg transition-colors"
|
|
117
|
+
{/* #209 R31 (mobile vertical rhythm — same pattern as R28/R30):
|
|
118
|
+
header row + quick-from chips row both mb-6 → mb-4 sm:mb-6.
|
|
119
|
+
#209 R33: header restructured to mirror /nodes R32 — title
|
|
120
|
+
cluster left (Messages + count chip + Export MD), magnifier
|
|
121
|
+
circle right. Inline ~290 px search input was moved into the
|
|
122
|
+
collapsible row below. */}
|
|
123
|
+
<div className="flex items-center gap-4 mb-4 sm:mb-6">
|
|
124
|
+
<div className="flex items-center gap-3 min-w-0 flex-1">
|
|
125
|
+
<h1 className="text-2xl font-bold text-white lg:ml-0 ml-10">Messages</h1>
|
|
126
|
+
{/* Round 89: filter-aware chip. When search/type filter is active
|
|
127
|
+
the visible rows are filtered.length; the total loaded is
|
|
128
|
+
messages.length (capped at 200 by useMessages). Show
|
|
129
|
+
`filtered / total` so users notice how much the filter is
|
|
130
|
+
hiding. Same pattern as r88 /tasks chip. */}
|
|
131
|
+
<span
|
|
132
|
+
className="text-xs bg-blue-900/30 text-blue-400 px-2 py-0.5 rounded-full border border-blue-800/30 tabular-nums shrink-0"
|
|
133
|
+
title={filtered.length < messages.length ? `Showing ${filtered.length} of ${messages.length} messages` : undefined}
|
|
134
134
|
>
|
|
135
|
-
|
|
136
|
-
</
|
|
137
|
-
|
|
135
|
+
{filtered.length < messages.length ? `${filtered.length} / ${messages.length}` : messages.length}
|
|
136
|
+
</span>
|
|
137
|
+
{filtered.length > 0 && (
|
|
138
|
+
<button
|
|
139
|
+
onClick={() => {
|
|
140
|
+
const md = filtered.map((m: MessageItem) =>
|
|
141
|
+
`**${m.from_alias || '?'}** → ${m.to_alias || '?'} (${m.type || '?'}) — ${m.created_at || ''}\n\n${m.content || '--'}\n\n---`
|
|
142
|
+
).join('\n\n');
|
|
143
|
+
const blob = new Blob([`# Messages Export\n\n${md}`], { type: 'text/markdown' });
|
|
144
|
+
const url = URL.createObjectURL(blob);
|
|
145
|
+
const a = document.createElement('a');
|
|
146
|
+
a.href = url; a.download = `messages-${new Date().toISOString().slice(0,10)}.md`;
|
|
147
|
+
a.click(); URL.revokeObjectURL(url);
|
|
148
|
+
}}
|
|
149
|
+
className="text-xs text-gray-500 hover:text-gray-300 border border-gray-700/50 px-2.5 py-1 rounded-lg transition-colors shrink-0"
|
|
150
|
+
>
|
|
151
|
+
Export MD
|
|
152
|
+
</button>
|
|
153
|
+
)}
|
|
154
|
+
</div>
|
|
155
|
+
<searchCtl.Button />
|
|
138
156
|
</div>
|
|
139
157
|
|
|
158
|
+
{/* #209 R34: shared <CollapsibleSearch> component. enabled gate
|
|
159
|
+
handles the no-content case (button + row both hide). */}
|
|
160
|
+
<searchCtl.Row />
|
|
161
|
+
|
|
140
162
|
{/* Round 76: hide search + type filter + view toggle + Debug button
|
|
141
163
|
when there are no messages at all. Same r70-class fix carried
|
|
142
164
|
across /nodes (r74) and /tasks (r75). When at least one message
|
|
143
165
|
exists, the chrome reappears so users can still clear filters. */}
|
|
144
166
|
{messages.length > 0 && (
|
|
145
167
|
<div className="flex flex-wrap gap-3 mb-3">
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
value={search}
|
|
149
|
-
onChange={e => setSearch(e.target.value)}
|
|
150
|
-
placeholder="Search from/to/content or use from:alias"
|
|
151
|
-
className="bg-[#111128] border border-[#2a2a4a] rounded-lg px-3 py-2 text-sm text-white placeholder-gray-600 focus:border-blue-500/50 focus:outline-none w-full sm:w-72"
|
|
152
|
-
/>
|
|
168
|
+
{/* R33: inline search input moved to the top-right magnifier
|
|
169
|
+
toggle above. Type select + view-mode toggle + Debug stay. */}
|
|
153
170
|
<select
|
|
154
171
|
value={filterType}
|
|
155
172
|
onChange={e => setFilterType(e.target.value)}
|
|
156
|
-
className="bg-[#111128] border border-[#2a2a4a] rounded-lg px-3 py-2 text-sm text-white focus:border-blue-500/50 focus:outline-none"
|
|
173
|
+
className="bg-[#111128] border border-[#2a2a4a] rounded-lg px-3 py-2 text-base sm:text-sm text-white focus:border-blue-500/50 focus:outline-none"
|
|
157
174
|
>
|
|
158
175
|
<option value="">All types</option>
|
|
159
176
|
<option value="task">task</option>
|
|
@@ -181,7 +198,7 @@ export default function MessagesPage() {
|
|
|
181
198
|
)}
|
|
182
199
|
|
|
183
200
|
{quickFromChips.length > 0 && (
|
|
184
|
-
<div className="mb-6 flex flex-wrap gap-2">
|
|
201
|
+
<div className="mb-4 sm:mb-6 flex flex-wrap gap-2">
|
|
185
202
|
{quickFromChips.map(alias => (
|
|
186
203
|
<button
|
|
187
204
|
key={alias}
|
|
@@ -260,19 +277,26 @@ export default function MessagesPage() {
|
|
|
260
277
|
|
|
261
278
|
return (
|
|
262
279
|
<div key={message.id}>
|
|
280
|
+
{/* R6 of #190 mobile polish: gap divider was my-4 (=16px
|
|
281
|
+
each side), and with multi-hour message lulls it
|
|
282
|
+
fired several times per session, eating ~32px each.
|
|
283
|
+
Halve it on mobile. */}
|
|
263
284
|
{gapExceeded && (
|
|
264
|
-
<div className="my-4 flex items-center gap-3">
|
|
285
|
+
<div className="my-2 sm:my-4 flex items-center gap-3">
|
|
265
286
|
<div className="h-px flex-1 bg-[#2a2a4a]" />
|
|
266
287
|
<div className="text-[11px] text-gray-600">{formatDividerLabel(message.created_at)}</div>
|
|
267
288
|
<div className="h-px flex-1 bg-[#2a2a4a]" />
|
|
268
289
|
</div>
|
|
269
290
|
)}
|
|
270
291
|
|
|
271
|
-
{/* Broadcasts span full width — no avatar gutter.
|
|
292
|
+
{/* Broadcasts span full width — no avatar gutter. R6 mobile:
|
|
293
|
+
tighter padding + tighter header margin + snug leading
|
|
294
|
+
on the content so each bubble is ~25-30% shorter at
|
|
295
|
+
390px. Desktop unchanged from sm: up. */}
|
|
272
296
|
{variant === 'broadcast' ? (
|
|
273
|
-
<div className={samePrev ? 'mt-1' : 'mt-3'}>
|
|
274
|
-
<div className="rounded-2xl border border-purple-500/20 bg-purple-500/10 px-4 py-3 shadow-sm w-full">
|
|
275
|
-
<div className="mb-2 flex flex-wrap items-center gap-2">
|
|
297
|
+
<div className={samePrev ? 'mt-0.5 sm:mt-1' : 'mt-2 sm:mt-3'}>
|
|
298
|
+
<div className="rounded-2xl border border-purple-500/20 bg-purple-500/10 px-3 py-2 sm:px-4 sm:py-3 shadow-sm w-full">
|
|
299
|
+
<div className="mb-1 sm:mb-2 flex flex-wrap items-center gap-2">
|
|
276
300
|
<span className={`text-xs px-2 py-0.5 rounded-md border ${TYPE_COLORS[message.type || ''] || 'bg-gray-500/10 text-gray-400 border-gray-500/20'}`}>
|
|
277
301
|
{message.type || 'unknown'}
|
|
278
302
|
</span>
|
|
@@ -285,24 +309,24 @@ export default function MessagesPage() {
|
|
|
285
309
|
long unbroken runs (URLs, ASCII rules like
|
|
286
310
|
`═══════════════`) wrap instead of pushing the
|
|
287
311
|
chat bubble past the mobile viewport. */}
|
|
288
|
-
<div className="whitespace-pre-wrap break-words [overflow-wrap:anywhere] text-sm leading-relaxed text-gray-200">
|
|
312
|
+
<div className="whitespace-pre-wrap break-words [overflow-wrap:anywhere] text-sm leading-snug sm:leading-relaxed text-gray-200">
|
|
289
313
|
{renderHighlighted(message.content, search)}
|
|
290
314
|
</div>
|
|
291
315
|
</div>
|
|
292
316
|
</div>
|
|
293
317
|
) : (
|
|
294
|
-
<div className={`${samePrev ? 'mt-1' : 'mt-3'} flex gap-2 ${variant === 'outgoing' ? 'flex-row-reverse' : 'flex-row'}`}>
|
|
318
|
+
<div className={`${samePrev ? 'mt-0.5 sm:mt-1' : 'mt-2 sm:mt-3'} flex gap-2 ${variant === 'outgoing' ? 'flex-row-reverse' : 'flex-row'}`}>
|
|
295
319
|
{/* Avatar gutter — fixed width keeps bubble columns aligned even on streaks */}
|
|
296
320
|
<div className="w-8 shrink-0 pt-1">
|
|
297
321
|
{!samePrev && <AliasAvatar alias={fromAlias} size={32} />}
|
|
298
322
|
</div>
|
|
299
|
-
<div className={`min-w-0 max-w-3xl rounded-2xl border px-4 py-3 shadow-sm ${
|
|
323
|
+
<div className={`min-w-0 max-w-3xl rounded-2xl border px-3 py-2 sm:px-4 sm:py-3 shadow-sm ${
|
|
300
324
|
variant === 'outgoing'
|
|
301
325
|
? 'border-green-500/20 bg-green-500/10'
|
|
302
326
|
: 'border-blue-500/20 bg-blue-500/10'
|
|
303
327
|
}`}>
|
|
304
328
|
{!samePrev && (
|
|
305
|
-
<div className="mb-2 flex flex-wrap items-center gap-2">
|
|
329
|
+
<div className="mb-1 sm:mb-2 flex flex-wrap items-center gap-2">
|
|
306
330
|
<span className={`text-xs px-2 py-0.5 rounded-md border ${TYPE_COLORS[message.type || ''] || 'bg-gray-500/10 text-gray-400 border-gray-500/20'}`}>
|
|
307
331
|
{message.type || 'unknown'}
|
|
308
332
|
</span>
|
|
@@ -320,7 +344,7 @@ export default function MessagesPage() {
|
|
|
320
344
|
long unbroken runs (URLs, ASCII rules like
|
|
321
345
|
`═══════════════`) wrap instead of pushing the
|
|
322
346
|
chat bubble past the mobile viewport. */}
|
|
323
|
-
<div className="whitespace-pre-wrap break-words [overflow-wrap:anywhere] text-sm leading-relaxed text-gray-200">
|
|
347
|
+
<div className="whitespace-pre-wrap break-words [overflow-wrap:anywhere] text-sm leading-snug sm:leading-relaxed text-gray-200">
|
|
324
348
|
{renderHighlighted(message.content, search)}
|
|
325
349
|
</div>
|
|
326
350
|
|
package/app/node/page.tsx
CHANGED
|
@@ -302,12 +302,12 @@ function NodeFullPanel({ alias, session, sse, sendMsg, setSendMsg, sending, send
|
|
|
302
302
|
onChange={e => setSendMsg(e.target.value)}
|
|
303
303
|
onKeyDown={e => e.key === 'Enter' && sendTask()}
|
|
304
304
|
placeholder={`Send task to ${alias}...`}
|
|
305
|
-
className="flex-1 bg-[#0a0a15] border border-[#2a2a4a] rounded px-3 py-2 text-xs text-white placeholder-gray-600 focus:border-blue-500 focus:outline-none"
|
|
305
|
+
className="flex-1 bg-[#0a0a15] border border-[#2a2a4a] rounded px-3 py-2 text-base sm:text-xs text-white placeholder-gray-600 focus:border-blue-500 focus:outline-none"
|
|
306
306
|
/>
|
|
307
307
|
<button
|
|
308
308
|
onClick={sendTask}
|
|
309
309
|
disabled={sending || !sendMsg.trim()}
|
|
310
|
-
className="px-
|
|
310
|
+
className="inline-flex min-h-[44px] items-center justify-center px-4 py-2 bg-blue-600 hover:bg-blue-500 disabled:bg-gray-700 text-white text-xs rounded transition-colors"
|
|
311
311
|
>
|
|
312
312
|
{sending ? '...' : 'Send'}
|
|
313
313
|
</button>
|
package/app/nodes/page.tsx
CHANGED
|
@@ -6,6 +6,7 @@ import { useSessions, useHealth } from '../lib/hooks';
|
|
|
6
6
|
import { TaskChatPanel } from '../components/TaskChatPanel';
|
|
7
7
|
import { EmptyState, NodesEmptyState } from '../components/EmptyState';
|
|
8
8
|
import { AliasAvatar } from '../components/AliasAvatar';
|
|
9
|
+
import { useCollapsibleSearch } from '../components/CollapsibleSearch';
|
|
9
10
|
import type { Session } from '../components/types';
|
|
10
11
|
import { SESSION_STATUS_CHIP_CLASS as STATUS_COLORS } from '../lib/status';
|
|
11
12
|
import { useChatUnread } from '../lib/chat-unread';
|
|
@@ -34,6 +35,17 @@ export default function NodesPage() {
|
|
|
34
35
|
const [search, setSearch] = useState('');
|
|
35
36
|
const [viewMode, setViewMode] = useState<ViewMode>('list');
|
|
36
37
|
const [chatAlias, setChatAlias] = useState<string | null>(null);
|
|
38
|
+
// #209 R32→R34: the WeChat-style magnifier-toggle search was hand-rolled
|
|
39
|
+
// inline in R32. R34 extracted it to a shared hook so /nodes, /messages,
|
|
40
|
+
// and any future search surface stay visually + behaviourally identical.
|
|
41
|
+
// The hook returns a Button (place in header) + Row (place after header).
|
|
42
|
+
const searchCtl = useCollapsibleSearch({
|
|
43
|
+
value: search,
|
|
44
|
+
onChange: setSearch,
|
|
45
|
+
placeholder: 'Search nodes…',
|
|
46
|
+
label: 'Search nodes',
|
|
47
|
+
enabled: sessions.length > 0,
|
|
48
|
+
});
|
|
37
49
|
|
|
38
50
|
const filtered: SessionRow[] = sessions
|
|
39
51
|
.map(s => ({ ...s, online: !!sseFor(s) }))
|
|
@@ -50,16 +62,32 @@ export default function NodesPage() {
|
|
|
50
62
|
|
|
51
63
|
return (
|
|
52
64
|
<div className="min-h-screen max-w-full overflow-x-hidden bg-[#0a0a1a] text-gray-100 p-4 sm:p-6 font-mono">
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
65
|
+
{/* #209 R31 (mobile vertical rhythm — goal "大幅提升移动端体验",
|
|
66
|
+
extending R28's Overview pattern + R30's /tasks pattern):
|
|
67
|
+
this header row + the status-bar wrapper + the filter row
|
|
68
|
+
below all drop mb-6 → mb-4 sm:mb-6. Three spots × 8 px =
|
|
69
|
+
~24 px scroll reclaim on /nodes before the first card.
|
|
70
|
+
#209 R32: search affordance moved to top-right magnifier
|
|
71
|
+
button (Vincent WeChat ref msg 563). justify-between makes
|
|
72
|
+
the title cluster hug left, button hugs right. */}
|
|
73
|
+
<div className="flex items-center gap-4 mb-4 sm:mb-6">
|
|
74
|
+
<div className="flex items-center gap-3 min-w-0 flex-1">
|
|
75
|
+
<h1 className="text-2xl font-bold text-white lg:ml-0 ml-10">Nodes</h1>
|
|
76
|
+
<span className="text-xs bg-green-900/30 text-green-400 px-2 py-0.5 rounded-full border border-green-800/30 shrink-0">
|
|
77
|
+
{onlineCount} online
|
|
78
|
+
</span>
|
|
79
|
+
<span className="text-xs bg-gray-900/30 text-gray-400 px-2 py-0.5 rounded-full border border-gray-800/30 shrink-0">
|
|
80
|
+
{sessions.length} total
|
|
81
|
+
</span>
|
|
82
|
+
</div>
|
|
83
|
+
<searchCtl.Button />
|
|
61
84
|
</div>
|
|
62
85
|
|
|
86
|
+
{/* #209 R34: shared <CollapsibleSearch> component handles row reveal,
|
|
87
|
+
autofocus, Escape, and Cancel. enabled=sessions.length>0 hides
|
|
88
|
+
everything until there's content to search. */}
|
|
89
|
+
<searchCtl.Row />
|
|
90
|
+
|
|
63
91
|
{/* Status bar */}
|
|
64
92
|
{sessions.length > 0 && (() => {
|
|
65
93
|
const working = filtered.filter(s => s.online && s.status === 'working').length;
|
|
@@ -67,7 +95,7 @@ export default function NodesPage() {
|
|
|
67
95
|
const offline = filtered.filter(s => !s.online).length;
|
|
68
96
|
const total = filtered.length || 1;
|
|
69
97
|
return (
|
|
70
|
-
<div className="mb-6">
|
|
98
|
+
<div className="mb-4 sm:mb-6">
|
|
71
99
|
<div className="flex h-2 rounded-full overflow-hidden bg-gray-800">
|
|
72
100
|
{working > 0 && <div className="bg-green-500" style={{ width: `${(working/total)*100}%` }} />}
|
|
73
101
|
{idle > 0 && <div className="bg-cyan-500" style={{ width: `${(idle/total)*100}%` }} />}
|
|
@@ -88,18 +116,14 @@ export default function NodesPage() {
|
|
|
88
116
|
exists, the chrome is back even if the current filter happens
|
|
89
117
|
to hide everything (so users can clear filters). */}
|
|
90
118
|
{sessions.length > 0 && (
|
|
91
|
-
<div className="flex flex-wrap gap-3 mb-6">
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
onChange={e => setSearch(e.target.value)}
|
|
96
|
-
placeholder="Search nodes..."
|
|
97
|
-
className="bg-[#111128] border border-[#2a2a4a] rounded-lg px-3 py-2 text-sm text-white placeholder-gray-600 focus:border-blue-500/50 focus:outline-none w-48"
|
|
98
|
-
/>
|
|
119
|
+
<div className="flex flex-wrap gap-3 mb-4 sm:mb-6">
|
|
120
|
+
{/* R32: the inline search input was moved to the top-right
|
|
121
|
+
magnifier toggle above. The status select + List/Grid
|
|
122
|
+
toggle stay here. */}
|
|
99
123
|
<select
|
|
100
124
|
value={filterStatus}
|
|
101
125
|
onChange={e => setFilterStatus(e.target.value)}
|
|
102
|
-
className="bg-[#111128] border border-[#2a2a4a] rounded-lg px-3 py-2 text-sm text-white focus:border-blue-500/50 focus:outline-none"
|
|
126
|
+
className="bg-[#111128] border border-[#2a2a4a] rounded-lg px-3 py-2 text-base sm:text-sm text-white focus:border-blue-500/50 focus:outline-none"
|
|
103
127
|
>
|
|
104
128
|
<option value="">All</option>
|
|
105
129
|
<option value="online">Online</option>
|
|
@@ -215,7 +239,7 @@ export default function NodesPage() {
|
|
|
215
239
|
})}
|
|
216
240
|
</div>
|
|
217
241
|
) : (
|
|
218
|
-
<div className="space-y-2">
|
|
242
|
+
<div className="space-y-1 sm:space-y-2">
|
|
219
243
|
{/* Round 94: AGENT + SERVER merged into one `agent · server`
|
|
220
244
|
cell. Round mobile-command: node row itself opens chat, so
|
|
221
245
|
the old Chat / Send Task action column is gone. */}
|
|
@@ -237,7 +261,7 @@ export default function NodesPage() {
|
|
|
237
261
|
tabIndex={0}
|
|
238
262
|
onClick={() => setChatAlias(s.alias)}
|
|
239
263
|
onKeyDown={e => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); setChatAlias(s.alias); } }}
|
|
240
|
-
className={`rounded-lg border border-[#2a2a4a] bg-[#111128] px-4 py-3 transition-colors hover:border-cyan-500/40 cursor-pointer ${!s.online ? 'opacity-50' : ''}`}
|
|
264
|
+
className={`rounded-lg border border-[#2a2a4a] bg-[#111128] px-3 py-2 sm:px-4 sm:py-3 transition-colors hover:border-cyan-500/40 cursor-pointer ${!s.online ? 'opacity-50' : ''}`}
|
|
241
265
|
>
|
|
242
266
|
<div className="hidden sm:grid sm:grid-cols-10 gap-2 items-center">
|
|
243
267
|
<div className="col-span-1">
|
|
@@ -261,7 +285,14 @@ export default function NodesPage() {
|
|
|
261
285
|
<div className="col-span-4 truncate text-xs text-gray-500" title={s.task || ''}>{s.task || '--'}</div>
|
|
262
286
|
<div className="col-span-1 text-xs text-gray-500">{timeAgo(s.last_seen_at || s.updated_at)}</div>
|
|
263
287
|
</div>
|
|
264
|
-
|
|
288
|
+
{/* R7 of #190: mobile node row was ~340px tall × ~150
|
|
289
|
+
rows = the 51k page. Wins this round, in priority
|
|
290
|
+
order: (1) drop the per-row "Tap anywhere to chat"
|
|
291
|
+
hint — useful once, redundant 149 times; the cyan
|
|
292
|
+
border on hover/focus still teaches it. (2) tighten
|
|
293
|
+
space-y-2 → space-y-1 so the avatar/task gap is
|
|
294
|
+
4px tighter on every row. */}
|
|
295
|
+
<div className="sm:hidden space-y-1">
|
|
265
296
|
<div className="flex items-center gap-2.5">
|
|
266
297
|
<div className="relative shrink-0">
|
|
267
298
|
<AliasAvatar alias={s.alias} size={28} />
|
|
@@ -279,7 +310,6 @@ export default function NodesPage() {
|
|
|
279
310
|
</span>
|
|
280
311
|
</div>
|
|
281
312
|
{s.task && <div className="truncate text-xs text-gray-500">{s.task}</div>}
|
|
282
|
-
<div className="text-[10px] text-cyan-300/70">Tap anywhere to chat</div>
|
|
283
313
|
</div>
|
|
284
314
|
</div>
|
|
285
315
|
);
|
package/app/page.tsx
CHANGED
|
@@ -4,10 +4,8 @@ import { useEffect, useState } from 'react';
|
|
|
4
4
|
import Link from 'next/link';
|
|
5
5
|
import { formatUptime, previewContent } from './components/utils';
|
|
6
6
|
import { StatsBar } from './components/StatsBar';
|
|
7
|
-
import { BroadcastBar } from './components/BroadcastBar';
|
|
8
7
|
import { TopoGraph } from './components/TopoGraph';
|
|
9
8
|
import { AgentCard } from './components/AgentCard';
|
|
10
|
-
import { InboxPanel } from './components/InboxPanel';
|
|
11
9
|
import { LoadingSkeleton } from './components/LoadingSkeleton';
|
|
12
10
|
import { NodesEmptyState as EmptyState } from './components/EmptyState';
|
|
13
11
|
import { AliasAvatar } from './components/AliasAvatar';
|
|
@@ -17,7 +15,6 @@ import { CommandCenter, useCommandCenter } from './components/CommandCenter';
|
|
|
17
15
|
import { DispatchPanel } from './components/DispatchPanel';
|
|
18
16
|
import { useSessions, useHealth, useAnetConfig, useTasks, useStats } from './lib/hooks';
|
|
19
17
|
import { useSSE } from './lib/useSSE';
|
|
20
|
-
import { InboxMessage } from './components/types';
|
|
21
18
|
import { useSWRConfig } from 'swr';
|
|
22
19
|
|
|
23
20
|
export default function Dashboard() {
|
|
@@ -40,7 +37,6 @@ export default function Dashboard() {
|
|
|
40
37
|
const [showConfig, setShowConfig] = useState(false);
|
|
41
38
|
const cmd = useCommandCenter();
|
|
42
39
|
const [showDispatch, setShowDispatch] = useState(false);
|
|
43
|
-
const [inbox, setInbox] = useState<InboxMessage[]>([]);
|
|
44
40
|
const [agentFilter, setAgentFilter] = useState<'all' | 'working' | 'idle' | 'offline'>('all');
|
|
45
41
|
// #84: last node.renamed event — passed to TopoGraph so an open chat
|
|
46
42
|
// popover follows the rename instead of pointing at a dead alias. `ts`
|
|
@@ -75,22 +71,6 @@ export default function Dashboard() {
|
|
|
75
71
|
},
|
|
76
72
|
});
|
|
77
73
|
|
|
78
|
-
// Fetch inbox (not in SWR since it accumulates)
|
|
79
|
-
useEffect(() => {
|
|
80
|
-
const fetchInbox = () => {
|
|
81
|
-
fetch('/api/hub/inbox').then(r => r.json()).then(data => {
|
|
82
|
-
if (data.messages?.length) setInbox(prev => {
|
|
83
|
-
const ids = new Set(prev.map(m => m.id));
|
|
84
|
-
const newMsgs = data.messages.filter((m: { id: string }) => !ids.has(m.id));
|
|
85
|
-
return [...newMsgs, ...prev].slice(0, 100);
|
|
86
|
-
});
|
|
87
|
-
}).catch(() => {});
|
|
88
|
-
};
|
|
89
|
-
fetchInbox();
|
|
90
|
-
const interval = setInterval(fetchInbox, 10000);
|
|
91
|
-
return () => clearInterval(interval);
|
|
92
|
-
}, []);
|
|
93
|
-
|
|
94
74
|
if (isLoading) return <LoadingSkeleton />;
|
|
95
75
|
|
|
96
76
|
const sseSessions = health?.sse_sessions || {};
|
|
@@ -156,8 +136,15 @@ export default function Dashboard() {
|
|
|
156
136
|
<div className="flex-1"><UserBar /></div>
|
|
157
137
|
</div>
|
|
158
138
|
|
|
159
|
-
{/* anet config (collapsed by default)
|
|
160
|
-
|
|
139
|
+
{/* anet config (collapsed by default).
|
|
140
|
+
#209 R28 (mobile vertical rhythm — goal "大幅提升移动端体验"):
|
|
141
|
+
this + Task Status + Nav rail + Recent Activity all drop their
|
|
142
|
+
24 px (mb-6) section gap to 16 px (mb-4) on phones, restoring
|
|
143
|
+
mb-6 from sm: up. Overview on mobile stacks 5-7 sections above
|
|
144
|
+
the agent grid; tightening 4 of those gaps reclaims ~32 px of
|
|
145
|
+
scroll. No content removed, no feature changed — desktop is
|
|
146
|
+
identical. */}
|
|
147
|
+
<section className="mb-4 sm:mb-6 rounded-lg border border-[#2a2a4a] bg-[#111128] px-4 py-3 shadow-lg shadow-black/20">
|
|
161
148
|
<button onClick={() => setShowConfig(!showConfig)} className="w-full flex items-center justify-between text-left">
|
|
162
149
|
<div className="flex items-center gap-2 text-xs">
|
|
163
150
|
<span className="uppercase text-gray-600">Config</span>
|
|
@@ -189,7 +176,7 @@ export default function Dashboard() {
|
|
|
189
176
|
|
|
190
177
|
{/* Task Status Stats */}
|
|
191
178
|
{Object.keys(taskStats).length > 0 && (
|
|
192
|
-
<section className="mb-6 rounded-lg border border-[#2a2a4a] bg-[#111128] px-4 py-3 shadow-lg shadow-black/20">
|
|
179
|
+
<section className="mb-4 sm:mb-6 rounded-lg border border-[#2a2a4a] bg-[#111128] px-4 py-3 shadow-lg shadow-black/20">
|
|
193
180
|
<div className="flex items-center justify-between mb-2">
|
|
194
181
|
<div className="text-xs uppercase text-gray-600">Task Status</div>
|
|
195
182
|
<Link href="/tasks" prefetch={false} className="text-xs text-cyan-400 hover:text-cyan-300">View all →</Link>
|
|
@@ -312,7 +299,7 @@ export default function Dashboard() {
|
|
|
312
299
|
</section>
|
|
313
300
|
|
|
314
301
|
{/* Nav rail — pure navigation, icon + label, no data */}
|
|
315
|
-
<section className="mb-6 grid grid-cols-3 gap-2 sm:gap-3">
|
|
302
|
+
<section className="mb-4 sm:mb-6 grid grid-cols-3 gap-2 sm:gap-3">
|
|
316
303
|
{[
|
|
317
304
|
{ href: '/messages', label: 'Messages', icon: 'M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z' },
|
|
318
305
|
{ href: '/logs', label: 'Audit log', icon: 'M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z M14 2v6h6 M16 13H8 M16 17H8 M10 9H8' },
|
|
@@ -328,11 +315,9 @@ export default function Dashboard() {
|
|
|
328
315
|
))}
|
|
329
316
|
</section>
|
|
330
317
|
|
|
331
|
-
<BroadcastBar />
|
|
332
|
-
|
|
333
318
|
{/* Recent Activity */}
|
|
334
319
|
{tasks.length > 0 && (
|
|
335
|
-
<section className="mb-6 bg-[#111128] border border-[#2a2a4a] rounded-xl p-4">
|
|
320
|
+
<section className="mb-4 sm:mb-6 bg-[#111128] border border-[#2a2a4a] rounded-xl p-4">
|
|
336
321
|
<div className="flex items-center justify-between mb-3">
|
|
337
322
|
<h2 className="text-sm font-semibold text-gray-300">Recent Activity</h2>
|
|
338
323
|
<Link href="/tasks" className="text-xs text-cyan-400 hover:text-cyan-300">All tasks →</Link>
|
|
@@ -454,8 +439,6 @@ export default function Dashboard() {
|
|
|
454
439
|
);
|
|
455
440
|
})()}
|
|
456
441
|
|
|
457
|
-
<InboxPanel messages={inbox} />
|
|
458
|
-
|
|
459
442
|
{/* Round 111 (issue #82): dropped the license badge — "trial (12d
|
|
460
443
|
left)" read like a paywall countdown on an open-source dashboard
|
|
461
444
|
and Vincent flagged it as misleading more than once. The SSE /
|