@sleep2agi/agent-network-dashboard 0.5.7-preview.3 → 0.5.7-preview.30
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 +48 -48
- 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]__0nw~zhp._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.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/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_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_1153xeb._.js +2 -2
- package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.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_server-logs_page_tsx_0dg.l_8._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_server-logs_page_tsx_0dg.l_8._.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/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/0.054rbp43q.y.js +1 -0
- 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/{15qxef.ilfysw.js → 03g.reu.n2vy-.js} +3 -3
- package/.next/static/chunks/0_8ca180mv271.css +1 -0
- package/.next/static/chunks/{0a.9~-nf0gpec.js → 0_eddxjio~tei.js} +1 -1
- package/.next/static/chunks/0_jpzi2lg4hja.js +1 -0
- package/.next/static/chunks/0bdp8t8hnj6tn.js +1 -0
- package/.next/static/chunks/0fkd-56.k2ts..js +1 -0
- package/.next/static/chunks/{0im751o4n61c7.js → 0hv6izw.g6cnm.js} +1 -1
- package/.next/static/chunks/{05uk96gc~9mni.js → 0ti3v67ixu43p.js} +1 -1
- package/.next/static/chunks/{0561vp5-q5.zp.js → 0ymogrg8t82.8.js} +1 -1
- package/.next/static/chunks/0z6y49-j~z6nf.js +7 -0
- package/.next/static/chunks/{0nqm.7w9_inwd.js → 0~eyvw9.ips4y.js} +2 -2
- package/.next/static/chunks/{0xqeurx5nvu76.js → 11noxrjsvu.~m.js} +1 -1
- package/.next/static/chunks/{0.66f3.rtcybb.js → 136r0ae9ihgvo.js} +1 -1
- 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 +1 -1
- package/app/components/AgentCard.tsx +19 -9
- package/app/components/AppShell.tsx +7 -1
- package/app/components/ChatPopover.tsx +1 -1
- 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 +12 -11
- package/app/components/TaskChatPanel.tsx +19 -5
- package/app/components/TaskDrawer.tsx +3 -1
- package/app/components/ThemeSwitcher.tsx +10 -79
- 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 +19 -12
- package/app/node/page.tsx +2 -2
- package/app/nodes/page.tsx +12 -6
- package/app/page.tsx +0 -24
- package/app/server-logs/page.tsx +10 -3
- package/app/settings/networks/page.tsx +3 -3
- package/app/settings/page.tsx +31 -83
- package/app/settings/tokens/page.tsx +1 -1
- package/app/tasks/page.tsx +13 -9
- package/bin/start.js +0 -0
- 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/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/static/chunks/03~5pxwbxxw-b.js +0 -1
- package/.next/static/chunks/0eggl7f36vd8m.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/0xze0l75jjpwr.js +0 -1
- 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/{cssa52keEzN4TEvVJY7V3 → 6N4r8gyt9Ff-j4i3EYFaz}/_buildManifest.js +0 -0
- /package/.next/static/{cssa52keEzN4TEvVJY7V3 → 6N4r8gyt9Ff-j4i3EYFaz}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{cssa52keEzN4TEvVJY7V3 → 6N4r8gyt9Ff-j4i3EYFaz}/_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
|
@@ -148,12 +148,12 @@ export default function MessagesPage() {
|
|
|
148
148
|
value={search}
|
|
149
149
|
onChange={e => setSearch(e.target.value)}
|
|
150
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"
|
|
151
|
+
className="bg-[#111128] border border-[#2a2a4a] rounded-lg px-3 py-2 text-base sm:text-sm text-white placeholder-gray-600 focus:border-blue-500/50 focus:outline-none w-full sm:w-72"
|
|
152
152
|
/>
|
|
153
153
|
<select
|
|
154
154
|
value={filterType}
|
|
155
155
|
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"
|
|
156
|
+
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
157
|
>
|
|
158
158
|
<option value="">All types</option>
|
|
159
159
|
<option value="task">task</option>
|
|
@@ -260,19 +260,26 @@ export default function MessagesPage() {
|
|
|
260
260
|
|
|
261
261
|
return (
|
|
262
262
|
<div key={message.id}>
|
|
263
|
+
{/* R6 of #190 mobile polish: gap divider was my-4 (=16px
|
|
264
|
+
each side), and with multi-hour message lulls it
|
|
265
|
+
fired several times per session, eating ~32px each.
|
|
266
|
+
Halve it on mobile. */}
|
|
263
267
|
{gapExceeded && (
|
|
264
|
-
<div className="my-4 flex items-center gap-3">
|
|
268
|
+
<div className="my-2 sm:my-4 flex items-center gap-3">
|
|
265
269
|
<div className="h-px flex-1 bg-[#2a2a4a]" />
|
|
266
270
|
<div className="text-[11px] text-gray-600">{formatDividerLabel(message.created_at)}</div>
|
|
267
271
|
<div className="h-px flex-1 bg-[#2a2a4a]" />
|
|
268
272
|
</div>
|
|
269
273
|
)}
|
|
270
274
|
|
|
271
|
-
{/* Broadcasts span full width — no avatar gutter.
|
|
275
|
+
{/* Broadcasts span full width — no avatar gutter. R6 mobile:
|
|
276
|
+
tighter padding + tighter header margin + snug leading
|
|
277
|
+
on the content so each bubble is ~25-30% shorter at
|
|
278
|
+
390px. Desktop unchanged from sm: up. */}
|
|
272
279
|
{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">
|
|
280
|
+
<div className={samePrev ? 'mt-0.5 sm:mt-1' : 'mt-2 sm:mt-3'}>
|
|
281
|
+
<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">
|
|
282
|
+
<div className="mb-1 sm:mb-2 flex flex-wrap items-center gap-2">
|
|
276
283
|
<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
284
|
{message.type || 'unknown'}
|
|
278
285
|
</span>
|
|
@@ -285,24 +292,24 @@ export default function MessagesPage() {
|
|
|
285
292
|
long unbroken runs (URLs, ASCII rules like
|
|
286
293
|
`═══════════════`) wrap instead of pushing the
|
|
287
294
|
chat bubble past the mobile viewport. */}
|
|
288
|
-
<div className="whitespace-pre-wrap break-words [overflow-wrap:anywhere] text-sm leading-relaxed text-gray-200">
|
|
295
|
+
<div className="whitespace-pre-wrap break-words [overflow-wrap:anywhere] text-sm leading-snug sm:leading-relaxed text-gray-200">
|
|
289
296
|
{renderHighlighted(message.content, search)}
|
|
290
297
|
</div>
|
|
291
298
|
</div>
|
|
292
299
|
</div>
|
|
293
300
|
) : (
|
|
294
|
-
<div className={`${samePrev ? 'mt-1' : 'mt-3'} flex gap-2 ${variant === 'outgoing' ? 'flex-row-reverse' : 'flex-row'}`}>
|
|
301
|
+
<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
302
|
{/* Avatar gutter — fixed width keeps bubble columns aligned even on streaks */}
|
|
296
303
|
<div className="w-8 shrink-0 pt-1">
|
|
297
304
|
{!samePrev && <AliasAvatar alias={fromAlias} size={32} />}
|
|
298
305
|
</div>
|
|
299
|
-
<div className={`min-w-0 max-w-3xl rounded-2xl border px-4 py-3 shadow-sm ${
|
|
306
|
+
<div className={`min-w-0 max-w-3xl rounded-2xl border px-3 py-2 sm:px-4 sm:py-3 shadow-sm ${
|
|
300
307
|
variant === 'outgoing'
|
|
301
308
|
? 'border-green-500/20 bg-green-500/10'
|
|
302
309
|
: 'border-blue-500/20 bg-blue-500/10'
|
|
303
310
|
}`}>
|
|
304
311
|
{!samePrev && (
|
|
305
|
-
<div className="mb-2 flex flex-wrap items-center gap-2">
|
|
312
|
+
<div className="mb-1 sm:mb-2 flex flex-wrap items-center gap-2">
|
|
306
313
|
<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
314
|
{message.type || 'unknown'}
|
|
308
315
|
</span>
|
|
@@ -320,7 +327,7 @@ export default function MessagesPage() {
|
|
|
320
327
|
long unbroken runs (URLs, ASCII rules like
|
|
321
328
|
`═══════════════`) wrap instead of pushing the
|
|
322
329
|
chat bubble past the mobile viewport. */}
|
|
323
|
-
<div className="whitespace-pre-wrap break-words [overflow-wrap:anywhere] text-sm leading-relaxed text-gray-200">
|
|
330
|
+
<div className="whitespace-pre-wrap break-words [overflow-wrap:anywhere] text-sm leading-snug sm:leading-relaxed text-gray-200">
|
|
324
331
|
{renderHighlighted(message.content, search)}
|
|
325
332
|
</div>
|
|
326
333
|
|
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
|
@@ -94,12 +94,12 @@ export default function NodesPage() {
|
|
|
94
94
|
value={search}
|
|
95
95
|
onChange={e => setSearch(e.target.value)}
|
|
96
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"
|
|
97
|
+
className="bg-[#111128] border border-[#2a2a4a] rounded-lg px-3 py-2 text-base sm:text-sm text-white placeholder-gray-600 focus:border-blue-500/50 focus:outline-none w-full sm:w-48"
|
|
98
98
|
/>
|
|
99
99
|
<select
|
|
100
100
|
value={filterStatus}
|
|
101
101
|
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"
|
|
102
|
+
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
103
|
>
|
|
104
104
|
<option value="">All</option>
|
|
105
105
|
<option value="online">Online</option>
|
|
@@ -215,7 +215,7 @@ export default function NodesPage() {
|
|
|
215
215
|
})}
|
|
216
216
|
</div>
|
|
217
217
|
) : (
|
|
218
|
-
<div className="space-y-2">
|
|
218
|
+
<div className="space-y-1 sm:space-y-2">
|
|
219
219
|
{/* Round 94: AGENT + SERVER merged into one `agent · server`
|
|
220
220
|
cell. Round mobile-command: node row itself opens chat, so
|
|
221
221
|
the old Chat / Send Task action column is gone. */}
|
|
@@ -237,7 +237,7 @@ export default function NodesPage() {
|
|
|
237
237
|
tabIndex={0}
|
|
238
238
|
onClick={() => setChatAlias(s.alias)}
|
|
239
239
|
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' : ''}`}
|
|
240
|
+
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
241
|
>
|
|
242
242
|
<div className="hidden sm:grid sm:grid-cols-10 gap-2 items-center">
|
|
243
243
|
<div className="col-span-1">
|
|
@@ -261,7 +261,14 @@ export default function NodesPage() {
|
|
|
261
261
|
<div className="col-span-4 truncate text-xs text-gray-500" title={s.task || ''}>{s.task || '--'}</div>
|
|
262
262
|
<div className="col-span-1 text-xs text-gray-500">{timeAgo(s.last_seen_at || s.updated_at)}</div>
|
|
263
263
|
</div>
|
|
264
|
-
|
|
264
|
+
{/* R7 of #190: mobile node row was ~340px tall × ~150
|
|
265
|
+
rows = the 51k page. Wins this round, in priority
|
|
266
|
+
order: (1) drop the per-row "Tap anywhere to chat"
|
|
267
|
+
hint — useful once, redundant 149 times; the cyan
|
|
268
|
+
border on hover/focus still teaches it. (2) tighten
|
|
269
|
+
space-y-2 → space-y-1 so the avatar/task gap is
|
|
270
|
+
4px tighter on every row. */}
|
|
271
|
+
<div className="sm:hidden space-y-1">
|
|
265
272
|
<div className="flex items-center gap-2.5">
|
|
266
273
|
<div className="relative shrink-0">
|
|
267
274
|
<AliasAvatar alias={s.alias} size={28} />
|
|
@@ -279,7 +286,6 @@ export default function NodesPage() {
|
|
|
279
286
|
</span>
|
|
280
287
|
</div>
|
|
281
288
|
{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
289
|
</div>
|
|
284
290
|
</div>
|
|
285
291
|
);
|
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 || {};
|
|
@@ -328,8 +308,6 @@ export default function Dashboard() {
|
|
|
328
308
|
))}
|
|
329
309
|
</section>
|
|
330
310
|
|
|
331
|
-
<BroadcastBar />
|
|
332
|
-
|
|
333
311
|
{/* Recent Activity */}
|
|
334
312
|
{tasks.length > 0 && (
|
|
335
313
|
<section className="mb-6 bg-[#111128] border border-[#2a2a4a] rounded-xl p-4">
|
|
@@ -454,8 +432,6 @@ export default function Dashboard() {
|
|
|
454
432
|
);
|
|
455
433
|
})()}
|
|
456
434
|
|
|
457
|
-
<InboxPanel messages={inbox} />
|
|
458
|
-
|
|
459
435
|
{/* Round 111 (issue #82): dropped the license badge — "trial (12d
|
|
460
436
|
left)" read like a paywall countdown on an open-source dashboard
|
|
461
437
|
and Vincent flagged it as misleading more than once. The SSE /
|
package/app/server-logs/page.tsx
CHANGED
|
@@ -196,7 +196,7 @@ export default function ServerLogsPage() {
|
|
|
196
196
|
value={search}
|
|
197
197
|
onChange={e => setSearch(e.target.value)}
|
|
198
198
|
placeholder="搜索关键字 (alias / task_id / error message)"
|
|
199
|
-
className="flex-1 min-w-[140px] basis-full sm:basis-0 px-3 py-1.5 text-xs bg-[#11111c] border border-[#2a2a4a] rounded text-gray-200 focus:outline-none focus:border-cyan-500/40"
|
|
199
|
+
className="flex-1 min-w-[140px] basis-full sm:basis-0 px-3 py-1.5 text-base sm:text-xs bg-[#11111c] border border-[#2a2a4a] rounded text-gray-200 focus:outline-none focus:border-cyan-500/40"
|
|
200
200
|
/>
|
|
201
201
|
<span className="text-[10px] text-gray-600">
|
|
202
202
|
{filtered.length} / {logs.length}
|
|
@@ -252,8 +252,15 @@ export default function ServerLogsPage() {
|
|
|
252
252
|
className="absolute left-0 top-0 bottom-0 w-0.5"
|
|
253
253
|
style={{ backgroundColor: LEVEL_STRIPE[l.level] }}
|
|
254
254
|
/>
|
|
255
|
-
|
|
256
|
-
|
|
255
|
+
{/* R4 of #190 mobile polish: the 100px timestamp column +
|
|
256
|
+
32px LEVEL badge ate ~45% of a 375px row, squeezing log
|
|
257
|
+
content into a 3-line wrap. Drop the ts column to 60px
|
|
258
|
+
on mobile (HH:MM:SS still fits at 9px), restore 100px
|
|
259
|
+
at sm. The 2px left rail (LEVEL_STRIPE) already
|
|
260
|
+
encodes level visually for warn/error, so the LOG badge
|
|
261
|
+
is redundant on mobile — hide it below sm. */}
|
|
262
|
+
<span className="text-gray-600 shrink-0 w-[60px] sm:w-[100px] text-[9px] sm:text-[10px] tabular-nums">{shortTime(l.ts)}</span>
|
|
263
|
+
<span className={`hidden sm:inline shrink-0 px-1.5 rounded border text-[9px] uppercase ${LEVEL_BADGE[l.level]}`}>
|
|
257
264
|
{l.level}
|
|
258
265
|
</span>
|
|
259
266
|
{/* Round 85: CommHub stamps each log line with a `[HH:MM:SS]`
|
|
@@ -111,9 +111,9 @@ export default function NetworksPage() {
|
|
|
111
111
|
<h2 className="text-sm font-semibold text-gray-300 mb-3">Create Network</h2>
|
|
112
112
|
<div className="space-y-3">
|
|
113
113
|
<input type="text" value={newName} onChange={e => setNewName(e.target.value)} placeholder="Network name"
|
|
114
|
-
className="w-full bg-[#0a0a15] border border-[#2a2a4a] rounded-lg px-3 py-2 text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:outline-none" />
|
|
114
|
+
className="w-full bg-[#0a0a15] border border-[#2a2a4a] rounded-lg px-3 py-2 text-base sm:text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:outline-none" />
|
|
115
115
|
<input type="text" value={newDesc} onChange={e => setNewDesc(e.target.value)} placeholder="Description (optional)"
|
|
116
|
-
className="w-full bg-[#0a0a15] border border-[#2a2a4a] rounded-lg px-3 py-2 text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:outline-none" />
|
|
116
|
+
className="w-full bg-[#0a0a15] border border-[#2a2a4a] rounded-lg px-3 py-2 text-base sm:text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:outline-none" />
|
|
117
117
|
<button onClick={createNetwork} disabled={!newName.trim()}
|
|
118
118
|
className="px-4 py-2 bg-green-600 hover:bg-green-500 disabled:bg-gray-800 text-white text-sm rounded-lg transition-colors">
|
|
119
119
|
Create
|
|
@@ -199,7 +199,7 @@ export default function NetworksPage() {
|
|
|
199
199
|
<div className="text-xs text-gray-500 mb-2">Invite</div>
|
|
200
200
|
<div className="flex gap-2">
|
|
201
201
|
<select value={inviteRole} onChange={e => setInviteRole(e.target.value)}
|
|
202
|
-
className="bg-[#0a0a15] border border-[#2a2a4a] rounded px-2 py-1 text-xs text-white focus:outline-none">
|
|
202
|
+
className="bg-[#0a0a15] border border-[#2a2a4a] rounded px-2 py-1 text-base sm:text-xs text-white focus:outline-none">
|
|
203
203
|
<option value="member">member</option>
|
|
204
204
|
<option value="admin">admin</option>
|
|
205
205
|
<option value="viewer">viewer</option>
|
package/app/settings/page.tsx
CHANGED
|
@@ -2,15 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
import { useState } from 'react';
|
|
4
4
|
import Link from 'next/link';
|
|
5
|
-
import { useAnetConfig, useHealth
|
|
5
|
+
import { useAnetConfig, useHealth } from '../lib/hooks';
|
|
6
6
|
import { DASHBOARD_VERSION } from '../lib/version';
|
|
7
7
|
|
|
8
8
|
export default function SettingsPage() {
|
|
9
9
|
const { config } = useAnetConfig();
|
|
10
10
|
const { health } = useHealth();
|
|
11
|
-
const { license: licData } = useLicense();
|
|
12
|
-
const [licKey, setLicKey] = useState('');
|
|
13
|
-
const [licResult, setLicResult] = useState('');
|
|
14
11
|
const [oldPwd, setOldPwd] = useState('');
|
|
15
12
|
const [newPwd, setNewPwd] = useState('');
|
|
16
13
|
const [pwdResult, setPwdResult] = useState('');
|
|
@@ -23,15 +20,18 @@ export default function SettingsPage() {
|
|
|
23
20
|
|
|
24
21
|
{/* Section anchor nav (round 28) — jump to a group instead of scrolling
|
|
25
22
|
through the whole page. Mirrors the section headers below; pages
|
|
26
|
-
with 7+ cards benefit from a visible table-of-contents.
|
|
27
|
-
|
|
23
|
+
with 7+ cards benefit from a visible table-of-contents.
|
|
24
|
+
R3 of #190 mobile polish: matches the /admin chip treatment
|
|
25
|
+
(preview.3) — 44px tap-target + visible border so the chips read
|
|
26
|
+
as tappable rather than as inert headings on 375–390px. */}
|
|
27
|
+
<nav className="mb-8 flex flex-wrap gap-2 text-xs">
|
|
28
28
|
{[
|
|
29
29
|
{ href: '#connection', label: 'Connection' },
|
|
30
30
|
{ href: '#account', label: 'Account' },
|
|
31
31
|
{ href: '#resources', label: 'Resources' },
|
|
32
32
|
].map(a => (
|
|
33
33
|
<a key={a.href} href={a.href}
|
|
34
|
-
className="rounded-md px-
|
|
34
|
+
className="inline-flex min-h-[44px] items-center rounded-md border border-[#2a2a4a] bg-[#0a0a15]/60 px-3 py-2 text-gray-400 hover:border-cyan-500/50 hover:text-cyan-300 hover:bg-cyan-500/10 transition-colors">
|
|
35
35
|
{a.label}
|
|
36
36
|
</a>
|
|
37
37
|
))}
|
|
@@ -131,87 +131,14 @@ export default function SettingsPage() {
|
|
|
131
131
|
<div className="flex-1 h-px bg-[#2a2a4a]" />
|
|
132
132
|
</div>
|
|
133
133
|
|
|
134
|
-
{/* License */}
|
|
135
|
-
<section className="bg-[#111128] border border-[#2a2a4a] rounded-xl p-5">
|
|
136
|
-
<h2 className="text-sm font-semibold text-gray-300 mb-4 flex items-center gap-2">
|
|
137
|
-
License
|
|
138
|
-
{licData?.license && (
|
|
139
|
-
<span
|
|
140
|
-
className={`inline-flex items-center gap-1.5 text-[10px] font-medium px-2 py-0.5 rounded-full border ${
|
|
141
|
-
licData.license.type === 'pro'
|
|
142
|
-
? 'text-green-300 bg-green-500/10 border-green-500/30'
|
|
143
|
-
: licData.license.days_left <= 7
|
|
144
|
-
? 'text-red-300 bg-red-500/10 border-red-500/30'
|
|
145
|
-
: 'text-amber-300 bg-amber-500/10 border-amber-500/30'
|
|
146
|
-
}`}
|
|
147
|
-
>
|
|
148
|
-
<span aria-hidden className="w-1.5 h-1.5 rounded-full bg-current" />
|
|
149
|
-
{licData.license.type}{licData.license.days_left ? ` · ${licData.license.days_left}d left` : ''}
|
|
150
|
-
</span>
|
|
151
|
-
)}
|
|
152
|
-
</h2>
|
|
153
|
-
{licData?.license ? (
|
|
154
|
-
<div className="space-y-3 text-sm">
|
|
155
|
-
{/* Type + Days Left are summarized in the inline chip in the
|
|
156
|
-
header. Surface "expiring soon" only when relevant. */}
|
|
157
|
-
{licData.license.days_left <= 7 && (
|
|
158
|
-
<div className={rowClass}>
|
|
159
|
-
<span className="text-red-400 font-medium">⚠ Expiring soon</span>
|
|
160
|
-
<span className="text-red-400">{licData.license.days_left} days left</span>
|
|
161
|
-
</div>
|
|
162
|
-
)}
|
|
163
|
-
<div className={rowClass}>
|
|
164
|
-
<span className="text-gray-500">Expires</span>
|
|
165
|
-
<span className={`text-gray-300 ${valueClass}`}>{licData.license.expires_at}</span>
|
|
166
|
-
</div>
|
|
167
|
-
{licData.limits && (
|
|
168
|
-
<>
|
|
169
|
-
<div className={rowClass}>
|
|
170
|
-
<span className="text-gray-500">Max Agents</span>
|
|
171
|
-
<span className={`text-gray-300 ${valueClass}`}>{licData.limits.max_agents}</span>
|
|
172
|
-
</div>
|
|
173
|
-
<div className={rowClass}>
|
|
174
|
-
<span className="text-gray-500">Max Networks</span>
|
|
175
|
-
<span className={`text-gray-300 ${valueClass}`}>{licData.limits.max_networks}</span>
|
|
176
|
-
</div>
|
|
177
|
-
<div className={rowClass}>
|
|
178
|
-
<span className="text-gray-500">Tasks/Day</span>
|
|
179
|
-
<span className={`text-gray-300 ${valueClass}`}>{licData.limits.max_tasks_day}</span>
|
|
180
|
-
</div>
|
|
181
|
-
</>
|
|
182
|
-
)}
|
|
183
|
-
<div className="pt-3 border-t border-[#2a2a4a]">
|
|
184
|
-
<div className="flex gap-2">
|
|
185
|
-
<input type="text" value={licKey} onChange={e => setLicKey(e.target.value)}
|
|
186
|
-
placeholder="anet-XXXX-XXXX-XXXX-XXXX"
|
|
187
|
-
className="flex-1 bg-[#0a0a15] border border-[#2a2a4a] rounded px-3 py-2 text-xs text-white placeholder-gray-600 focus:outline-none" />
|
|
188
|
-
<button onClick={async () => {
|
|
189
|
-
if (!licKey.trim()) return;
|
|
190
|
-
const res = await fetch('/api/hub/license', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ key: licKey }) });
|
|
191
|
-
const data = await res.json();
|
|
192
|
-
setLicResult(data.ok ? `Activated: ${data.type}` : `Failed: ${data.error}`);
|
|
193
|
-
if (data.ok) setLicKey('');
|
|
194
|
-
setTimeout(() => setLicResult(''), 5000);
|
|
195
|
-
}} className="px-3 py-2 bg-cyan-600 hover:bg-cyan-500 text-white text-xs rounded transition-colors">
|
|
196
|
-
Activate
|
|
197
|
-
</button>
|
|
198
|
-
</div>
|
|
199
|
-
{licResult && <div className={`mt-2 text-xs ${licResult.startsWith('Failed') ? 'text-red-400' : 'text-green-400'}`}>{licResult}</div>}
|
|
200
|
-
</div>
|
|
201
|
-
</div>
|
|
202
|
-
) : (
|
|
203
|
-
<div className="text-xs text-gray-600">License info not available</div>
|
|
204
|
-
)}
|
|
205
|
-
</section>
|
|
206
|
-
|
|
207
134
|
{/* Change Password */}
|
|
208
135
|
<section className="bg-[#111128] border border-[#2a2a4a] rounded-xl p-5">
|
|
209
136
|
<h2 className="text-sm font-semibold text-gray-300 mb-4">Change Password</h2>
|
|
210
137
|
<div className="space-y-3">
|
|
211
138
|
<input type="password" value={oldPwd} onChange={e => setOldPwd(e.target.value)} placeholder="Current password"
|
|
212
|
-
className="w-full bg-[#0a0a15] border border-[#2a2a4a] rounded-lg px-3 py-2 text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:outline-none" />
|
|
139
|
+
className="w-full bg-[#0a0a15] border border-[#2a2a4a] rounded-lg px-3 py-2 text-base sm:text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:outline-none" />
|
|
213
140
|
<input type="password" value={newPwd} onChange={e => setNewPwd(e.target.value)} placeholder="New password"
|
|
214
|
-
className="w-full bg-[#0a0a15] border border-[#2a2a4a] rounded-lg px-3 py-2 text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:outline-none" />
|
|
141
|
+
className="w-full bg-[#0a0a15] border border-[#2a2a4a] rounded-lg px-3 py-2 text-base sm:text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:outline-none" />
|
|
215
142
|
<button onClick={async () => {
|
|
216
143
|
if (!oldPwd || !newPwd) return;
|
|
217
144
|
const saved = sessionStorage.getItem('anet_v3_auth');
|
|
@@ -263,7 +190,13 @@ export default function SettingsPage() {
|
|
|
263
190
|
<div className="flex-1 h-px bg-[#2a2a4a]" />
|
|
264
191
|
</div>
|
|
265
192
|
|
|
266
|
-
{/*
|
|
193
|
+
{/* #209 (per Vincent 521/522): the low-frequency pages 通信龙
|
|
194
|
+
moved out of the sidebar (Messages, Audit Log, Server Logs)
|
|
195
|
+
need a discovery surface that isn't the primary nav — folded
|
|
196
|
+
into the Settings/Resources grid alongside Tokens + Networks
|
|
197
|
+
so the page tree stays the same (pages NOT deleted), the
|
|
198
|
+
sidebar stays at 6, and users still have a one-tap way to
|
|
199
|
+
reach them without remembering URLs or opening Cmd+K. */}
|
|
267
200
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
268
201
|
<Link href="/settings/tokens" className="bg-[#111128] border border-[#2a2a4a] rounded-xl p-5 hover:border-cyan-500/30 transition-colors">
|
|
269
202
|
<h2 className="text-sm font-semibold text-gray-300">API Tokens</h2>
|
|
@@ -275,6 +208,21 @@ export default function SettingsPage() {
|
|
|
275
208
|
<p className="text-xs text-gray-500 mt-2">Create, manage, and delete agent networks.</p>
|
|
276
209
|
<span className="text-xs text-cyan-400 mt-3 inline-block">Manage →</span>
|
|
277
210
|
</Link>
|
|
211
|
+
<Link href="/messages" className="bg-[#111128] border border-[#2a2a4a] rounded-xl p-5 hover:border-cyan-500/30 transition-colors">
|
|
212
|
+
<h2 className="text-sm font-semibold text-gray-300">Messages</h2>
|
|
213
|
+
<p className="text-xs text-gray-500 mt-2">Global timeline of CommHub messages across all agents.</p>
|
|
214
|
+
<span className="text-xs text-cyan-400 mt-3 inline-block">Open →</span>
|
|
215
|
+
</Link>
|
|
216
|
+
<Link href="/logs" className="bg-[#111128] border border-[#2a2a4a] rounded-xl p-5 hover:border-cyan-500/30 transition-colors">
|
|
217
|
+
<h2 className="text-sm font-semibold text-gray-300">Audit Log</h2>
|
|
218
|
+
<p className="text-xs text-gray-500 mt-2">Authentication, token rotations, and admin actions.</p>
|
|
219
|
+
<span className="text-xs text-cyan-400 mt-3 inline-block">Open →</span>
|
|
220
|
+
</Link>
|
|
221
|
+
<Link href="/server-logs" className="bg-[#111128] border border-[#2a2a4a] rounded-xl p-5 hover:border-cyan-500/30 transition-colors">
|
|
222
|
+
<h2 className="text-sm font-semibold text-gray-300">Server Logs</h2>
|
|
223
|
+
<p className="text-xs text-gray-500 mt-2">Live stdout / stderr from the CommHub server.</p>
|
|
224
|
+
<span className="text-xs text-cyan-400 mt-3 inline-block">Open →</span>
|
|
225
|
+
</Link>
|
|
278
226
|
</div>
|
|
279
227
|
</div>
|
|
280
228
|
|
|
@@ -80,7 +80,7 @@ export default function TokensPage() {
|
|
|
80
80
|
<div className="flex gap-2">
|
|
81
81
|
<input type="text" value={newName} onChange={e => setNewName(e.target.value)}
|
|
82
82
|
placeholder="Token name (e.g. my-cli)"
|
|
83
|
-
className="flex-1 bg-[#0a0a15] border border-[#2a2a4a] rounded-lg px-3 py-2 text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:outline-none" />
|
|
83
|
+
className="flex-1 bg-[#0a0a15] border border-[#2a2a4a] rounded-lg px-3 py-2 text-base sm:text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:outline-none" />
|
|
84
84
|
<button onClick={createToken} disabled={!newName.trim()}
|
|
85
85
|
className="px-4 py-2 bg-cyan-600 hover:bg-cyan-500 disabled:bg-gray-800 text-white text-sm rounded-lg transition-colors">
|
|
86
86
|
Create
|
package/app/tasks/page.tsx
CHANGED
|
@@ -180,7 +180,7 @@ function TasksContent() {
|
|
|
180
180
|
value={filterFrom}
|
|
181
181
|
onChange={e => setFilterFrom(e.target.value)}
|
|
182
182
|
placeholder="any node"
|
|
183
|
-
className="w-28 bg-transparent text-sm text-white placeholder-gray-700 focus:outline-none"
|
|
183
|
+
className="w-28 bg-transparent text-base sm:text-sm text-white placeholder-gray-700 focus:outline-none"
|
|
184
184
|
/>
|
|
185
185
|
</div>
|
|
186
186
|
<div className="flex items-center gap-1.5 rounded-lg border border-[#2a2a4a] bg-[#111128] px-2.5 py-1.5 focus-within:border-blue-500/40">
|
|
@@ -190,7 +190,7 @@ function TasksContent() {
|
|
|
190
190
|
value={filterTo}
|
|
191
191
|
onChange={e => setFilterTo(e.target.value)}
|
|
192
192
|
placeholder="any node"
|
|
193
|
-
className="w-28 bg-transparent text-sm text-white placeholder-gray-700 focus:outline-none"
|
|
193
|
+
className="w-28 bg-transparent text-base sm:text-sm text-white placeholder-gray-700 focus:outline-none"
|
|
194
194
|
/>
|
|
195
195
|
</div>
|
|
196
196
|
{(filterStatus || filterFrom || filterTo) && (
|
|
@@ -248,7 +248,7 @@ function TasksContent() {
|
|
|
248
248
|
: 'Tasks will appear here when agents send them via CommHub.'}
|
|
249
249
|
/>
|
|
250
250
|
) : (
|
|
251
|
-
<div className="space-y-2">
|
|
251
|
+
<div className="space-y-1 sm:space-y-2">
|
|
252
252
|
{/* Table header */}
|
|
253
253
|
<div className="hidden sm:grid sm:grid-cols-12 gap-2 px-4 py-2 text-xs text-gray-600 uppercase">
|
|
254
254
|
<div className="col-span-1">Status</div>
|
|
@@ -264,7 +264,7 @@ function TasksContent() {
|
|
|
264
264
|
return (
|
|
265
265
|
<div
|
|
266
266
|
key={t.task_id}
|
|
267
|
-
className={`anet-task-row group bg-[#111128] border rounded-lg px-4 py-3 transition-all duration-200 cursor-pointer ${
|
|
267
|
+
className={`anet-task-row group bg-[#111128] border rounded-lg px-3 py-2 sm:px-4 sm:py-3 transition-all duration-200 cursor-pointer ${
|
|
268
268
|
isOpen
|
|
269
269
|
? 'border-[#3a3a5a] shadow-lg shadow-black/20'
|
|
270
270
|
: 'border-[#2a2a4a] hover:border-[#3a3a5a] hover:bg-[#15152e]'
|
|
@@ -311,8 +311,12 @@ function TasksContent() {
|
|
|
311
311
|
</div>
|
|
312
312
|
</div>
|
|
313
313
|
|
|
314
|
-
{/* Mobile layout
|
|
315
|
-
|
|
314
|
+
{/* Mobile layout — R8 of #190 mobile polish: 4-row stack
|
|
315
|
+
→ 3-row by inlining timeAgo onto the same row as the
|
|
316
|
+
from→to alias header (it's already 4 small atoms, has
|
|
317
|
+
room), plus space-y-2 → space-y-1 to trim ~4px per
|
|
318
|
+
row × 200 tasks. */}
|
|
319
|
+
<div className="sm:hidden space-y-1">
|
|
316
320
|
<div className="flex items-center justify-between">
|
|
317
321
|
<span className={statusBadge(t.status)}>{t.status}</span>
|
|
318
322
|
<div className="flex items-center gap-2">
|
|
@@ -328,13 +332,13 @@ function TasksContent() {
|
|
|
328
332
|
</div>
|
|
329
333
|
<div className="flex items-center gap-1.5 text-xs text-gray-300 min-w-0">
|
|
330
334
|
{t.from_name && <AliasAvatar alias={t.from_name} size={16} />}
|
|
331
|
-
<span className="truncate max-w-[
|
|
335
|
+
<span className="truncate max-w-[35%]">{t.from_name || '--'}</span>
|
|
332
336
|
<span className="text-gray-600">→</span>
|
|
333
337
|
{t.to_name && <AliasAvatar alias={t.to_name} size={16} />}
|
|
334
|
-
<span className="truncate max-w-[
|
|
338
|
+
<span className="truncate max-w-[35%]">{t.to_name || '--'}</span>
|
|
339
|
+
<span className="ml-auto shrink-0 text-[10px] text-gray-600">{timeAgo(t.created_at)}</span>
|
|
335
340
|
</div>
|
|
336
341
|
<div className="text-xs text-gray-400 line-clamp-1" title={t.content}>{previewContent(t.content)}</div>
|
|
337
|
-
<div className="text-xs text-gray-600">{timeAgo(t.created_at)}</div>
|
|
338
342
|
</div>
|
|
339
343
|
|
|
340
344
|
{/* Expanded detail — always mounted; grid-rows 0fr↔1fr trick gives
|
package/bin/start.js
CHANGED
|
File without changes
|