@sleep2agi/agent-network-dashboard 0.5.7-preview.7 → 0.5.7-preview.73
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/BUILD_ID +1 -1
- package/.next/app-path-routes-manifest.json +0 -1
- package/.next/build-manifest.json +3 -3
- package/.next/diagnostics/route-bundle-stats.json +65 -65
- package/.next/fallback-build-manifest.json +3 -3
- package/.next/prerender-manifest.json +3 -3
- package/.next/routes-manifest.json +0 -6
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page/next-font-manifest.json +2 -1
- package/.next/server/app/_not-found/page.js +1 -1
- package/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +4 -4
- package/.next/server/app/_not-found.rsc +16 -16
- package/.next/server/app/_not-found.segments/_full.segment.rsc +16 -16
- package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/server/app/_not-found.segments/_index.segment.rsc +9 -9
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +3 -3
- package/.next/server/app/admin/page/next-font-manifest.json +2 -1
- package/.next/server/app/admin/page.js +1 -1
- package/.next/server/app/admin/page.js.nft.json +1 -1
- package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
- package/.next/server/app/admin.html +4 -4
- package/.next/server/app/admin.rsc +20 -19
- package/.next/server/app/admin.segments/_full.segment.rsc +20 -19
- package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
- package/.next/server/app/admin.segments/_index.segment.rsc +9 -9
- package/.next/server/app/admin.segments/_tree.segment.rsc +5 -4
- package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/admin.segments/admin.segment.rsc +3 -3
- package/.next/server/app/index.html +4 -4
- package/.next/server/app/index.rsc +20 -19
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/index.segments/_full.segment.rsc +20 -19
- package/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/server/app/index.segments/_index.segment.rsc +9 -9
- package/.next/server/app/index.segments/_tree.segment.rsc +5 -4
- package/.next/server/app/login/page/next-font-manifest.json +2 -1
- package/.next/server/app/login/page.js +1 -1
- package/.next/server/app/login/page.js.nft.json +1 -1
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/login.html +2 -2
- package/.next/server/app/login.rsc +20 -19
- package/.next/server/app/login.segments/_full.segment.rsc +20 -19
- package/.next/server/app/login.segments/_head.segment.rsc +4 -4
- package/.next/server/app/login.segments/_index.segment.rsc +9 -9
- package/.next/server/app/login.segments/_tree.segment.rsc +5 -4
- package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/login.segments/login.segment.rsc +3 -3
- package/.next/server/app/logs/page/next-font-manifest.json +2 -1
- package/.next/server/app/logs/page.js +1 -1
- package/.next/server/app/logs/page.js.nft.json +1 -1
- package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/logs.html +4 -4
- package/.next/server/app/logs.rsc +20 -19
- package/.next/server/app/logs.segments/_full.segment.rsc +20 -19
- package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
- package/.next/server/app/logs.segments/_index.segment.rsc +9 -9
- package/.next/server/app/logs.segments/_tree.segment.rsc +5 -4
- package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
- package/.next/server/app/manifest.webmanifest.body +1 -1
- package/.next/server/app/messages/page/next-font-manifest.json +2 -1
- package/.next/server/app/messages/page.js +1 -1
- package/.next/server/app/messages/page.js.nft.json +1 -1
- package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
- package/.next/server/app/messages.html +4 -4
- package/.next/server/app/messages.rsc +20 -19
- package/.next/server/app/messages.segments/_full.segment.rsc +20 -19
- package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
- package/.next/server/app/messages.segments/_index.segment.rsc +9 -9
- package/.next/server/app/messages.segments/_tree.segment.rsc +5 -4
- package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
- package/.next/server/app/node/page/next-font-manifest.json +2 -1
- package/.next/server/app/node/page.js +1 -1
- package/.next/server/app/node/page.js.nft.json +1 -1
- package/.next/server/app/node/page_client-reference-manifest.js +1 -1
- package/.next/server/app/node.html +4 -4
- package/.next/server/app/node.rsc +20 -19
- package/.next/server/app/node.segments/_full.segment.rsc +20 -19
- package/.next/server/app/node.segments/_head.segment.rsc +4 -4
- package/.next/server/app/node.segments/_index.segment.rsc +9 -9
- package/.next/server/app/node.segments/_tree.segment.rsc +5 -4
- package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/node.segments/node.segment.rsc +3 -3
- package/.next/server/app/nodes/page/next-font-manifest.json +2 -1
- package/.next/server/app/nodes/page.js +1 -1
- package/.next/server/app/nodes/page.js.nft.json +1 -1
- package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
- package/.next/server/app/nodes.html +4 -4
- package/.next/server/app/nodes.rsc +20 -19
- package/.next/server/app/nodes.segments/_full.segment.rsc +20 -19
- package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
- package/.next/server/app/nodes.segments/_index.segment.rsc +9 -9
- package/.next/server/app/nodes.segments/_tree.segment.rsc +5 -4
- package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
- package/.next/server/app/page/next-font-manifest.json +2 -1
- package/.next/server/app/page.js +1 -1
- package/.next/server/app/page.js.nft.json +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/server-logs/page/next-font-manifest.json +2 -1
- package/.next/server/app/server-logs/page.js +1 -1
- package/.next/server/app/server-logs/page.js.nft.json +1 -1
- package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/server-logs.html +4 -4
- package/.next/server/app/server-logs.rsc +20 -19
- package/.next/server/app/server-logs.segments/_full.segment.rsc +20 -19
- package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
- package/.next/server/app/server-logs.segments/_index.segment.rsc +9 -9
- package/.next/server/app/server-logs.segments/_tree.segment.rsc +5 -4
- package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
- package/.next/server/app/servers/page/next-font-manifest.json +2 -1
- package/.next/server/app/servers/page.js +1 -1
- package/.next/server/app/servers/page.js.nft.json +1 -1
- package/.next/server/app/servers/page_client-reference-manifest.js +1 -1
- package/.next/server/app/servers.html +4 -4
- package/.next/server/app/servers.rsc +20 -19
- package/.next/server/app/servers.segments/_full.segment.rsc +20 -19
- package/.next/server/app/servers.segments/_head.segment.rsc +4 -4
- package/.next/server/app/servers.segments/_index.segment.rsc +9 -9
- package/.next/server/app/servers.segments/_tree.segment.rsc +5 -4
- package/.next/server/app/servers.segments/servers/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/servers.segments/servers.segment.rsc +3 -3
- package/.next/server/app/settings/networks/page/next-font-manifest.json +2 -1
- package/.next/server/app/settings/networks/page.js +1 -1
- package/.next/server/app/settings/networks/page.js.nft.json +1 -1
- package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/networks.html +4 -4
- package/.next/server/app/settings/networks.rsc +20 -19
- package/.next/server/app/settings/networks.segments/_full.segment.rsc +20 -19
- package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings/networks.segments/_index.segment.rsc +9 -9
- package/.next/server/app/settings/networks.segments/_tree.segment.rsc +5 -4
- package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
- package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
- package/.next/server/app/settings/page/next-font-manifest.json +2 -1
- package/.next/server/app/settings/page.js +1 -1
- package/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/tokens/page/next-font-manifest.json +2 -1
- package/.next/server/app/settings/tokens/page.js +1 -1
- package/.next/server/app/settings/tokens/page.js.nft.json +1 -1
- package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/tokens.html +4 -4
- package/.next/server/app/settings/tokens.rsc +20 -19
- package/.next/server/app/settings/tokens.segments/_full.segment.rsc +20 -19
- package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings/tokens.segments/_index.segment.rsc +9 -9
- package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +5 -4
- package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
- package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
- package/.next/server/app/settings.html +4 -4
- package/.next/server/app/settings.rsc +20 -19
- package/.next/server/app/settings.segments/_full.segment.rsc +20 -19
- package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings.segments/_index.segment.rsc +9 -9
- package/.next/server/app/settings.segments/_tree.segment.rsc +5 -4
- package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
- package/.next/server/app/tasks/[id]/page/next-font-manifest.json +2 -1
- package/.next/server/app/tasks/[id]/page.js +1 -1
- package/.next/server/app/tasks/[id]/page.js.nft.json +1 -1
- package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/tasks/page/next-font-manifest.json +2 -1
- package/.next/server/app/tasks/page.js +1 -1
- package/.next/server/app/tasks/page.js.nft.json +1 -1
- package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
- package/.next/server/app/tasks.html +4 -4
- package/.next/server/app/tasks.rsc +20 -19
- package/.next/server/app/tasks.segments/_full.segment.rsc +20 -19
- package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
- package/.next/server/app/tasks.segments/_index.segment.rsc +9 -9
- package/.next/server/app/tasks.segments/_tree.segment.rsc +5 -4
- package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
- package/.next/server/app-paths-manifest.json +0 -1
- package/.next/server/chunks/00jm_next_dist_0ju_ux9._.js +1 -1
- package/.next/server/chunks/00jm_next_dist_0ju_ux9._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__030vg4n._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__030vg4n._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__04gz75y._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__04gz75y._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__05kf31s._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__05kf31s._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__096ytyk._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__096ytyk._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0u4-66w._.js +8 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0u4-66w._.js.map +1 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +3 -3
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0_870i8._.js +3 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0_870i8._.js.map +1 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0fjlnh~._.js +3 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0fjlnh~._.js.map +1 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0gd.4pc._.js +9 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0gd.4pc._.js.map +1 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0wn4jc5._.js +3 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0wn4jc5._.js.map +1 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0r7kb.o._.js +9 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0r7kb.o._.js.map +1 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_servers_page_tsx_0jib5qm._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_servers_page_tsx_0jib5qm._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_tasks_page_tsx_0mwxy4z._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_tasks_page_tsx_0mwxy4z._.js.map +1 -1
- package/.next/server/middleware-build-manifest.js +3 -3
- package/.next/server/next-font-manifest.js +1 -1
- package/.next/server/next-font-manifest.json +30 -15
- package/.next/server/pages/404.html +4 -4
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.js +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/0..h8s._z~uek.js +1 -0
- package/.next/static/chunks/0.mh8n0itrii5.js +1 -0
- package/.next/static/chunks/{0jp~cs9-zkmqa.js → 00b4y77vxfabl.js} +1 -1
- package/.next/static/chunks/049vx3qljs1tt.js +1 -0
- package/.next/static/chunks/066jf0nk75nic.css +2 -0
- package/.next/static/chunks/06vp7429lrzl7.js +1 -0
- package/.next/static/chunks/06xxn73qy0qiz.js +7 -0
- package/.next/static/chunks/0_bn~gcrgo.4n.js +1 -0
- package/.next/static/chunks/0_tvbie.c68h5.js +1 -0
- package/.next/static/chunks/0cp0cz3mxejl~.js +4 -0
- package/.next/static/chunks/0g4d-_fi-d9hg.js +1 -0
- package/.next/static/chunks/0scww97p6z5z..css +1 -0
- package/.next/static/chunks/0shtnff1p8hzw.js +1 -0
- package/.next/static/chunks/0wz0122ym_gr3.js +1 -0
- package/.next/static/chunks/0y5gol09tlu63.js +1 -0
- package/.next/static/chunks/13l-ngu707546.js +1 -0
- package/.next/static/chunks/14141xj5.1t3t.js +1 -0
- package/.next/static/chunks/149a4l50_3vw-.js +7 -0
- package/.next/static/chunks/152_p6jt7txp6.js +1 -0
- package/.next/static/chunks/15hos-r_t7doi.js +1 -0
- package/.next/static/media/4fa387ec64143e14-s.0wkzw~je483f-.woff2 +0 -0
- package/.next/static/media/53b9e256198e5412-s.0-wfv7uh4i7h9.woff2 +0 -0
- package/.next/static/media/5ce348bf30bf5439-s.0zgw-jeven.3w.woff2 +0 -0
- package/.next/static/media/6306c77e7c8268e4-s.0rhz0arwfsn~5.woff2 +0 -0
- package/.next/static/media/7178b3e590c64307-s.0nx0ww8fni_q3.woff2 +0 -0
- package/.next/static/media/797e433ab948586e-s.p.08e28id.o-okb.woff2 +0 -0
- package/.next/static/media/7d817b4c03b0c5f1-s.0l76wvqk9d84w.woff2 +0 -0
- package/.next/static/media/8a480f0b521d4e75-s.0jzbimsg8vl84.woff2 +0 -0
- package/.next/static/media/bbc41e54d2fcbd21-s.0k4k9394f2q-k.woff2 +0 -0
- package/.next/static/media/caa3a2e1cccd8315-s.p.09~u27dqhyhd6.woff2 +0 -0
- package/.next/static/media/fef07dbb0973bf53-s.12tyk43_3sh9u.woff2 +0 -0
- package/.next/trace +2 -2
- package/.next/trace-build +1 -1
- package/.next/types/routes.d.ts +1 -2
- package/.next/types/validator.ts +0 -9
- package/app/admin/page.tsx +53 -38
- package/app/components/AgentCard.tsx +36 -20
- package/app/components/AppShell.tsx +7 -1
- package/app/components/ChatPopover.tsx +1 -1
- package/app/components/CollapsibleSearch.tsx +127 -0
- package/app/components/CommandCenter.tsx +14 -4
- package/app/components/CommandPalette.tsx +6 -6
- package/app/components/DispatchPanel.tsx +13 -10
- package/app/components/EmptyState.tsx +19 -4
- package/app/components/HealthBanner.tsx +28 -4
- package/app/components/HelpOverlay.tsx +4 -7
- package/app/components/LoadingSkeleton.tsx +31 -21
- package/app/components/MobileNav.tsx +28 -21
- package/app/components/Sidebar.tsx +30 -23
- package/app/components/StatsBar.tsx +69 -42
- package/app/components/TaskChatPanel.tsx +19 -5
- package/app/components/TaskDrawer.tsx +9 -7
- package/app/components/ThemeSwitcher.tsx +15 -79
- package/app/components/TopoGraph.tsx +31 -21
- package/app/components/UserBar.tsx +5 -5
- package/app/globals.css +1757 -1776
- package/app/layout.tsx +37 -4
- package/app/lib/hooks.ts +0 -5
- package/app/lib/status.ts +24 -17
- package/app/login/page.tsx +7 -7
- package/app/logs/page.tsx +12 -6
- package/app/manifest.ts +2 -2
- package/app/messages/page.tsx +67 -47
- package/app/node/page.tsx +27 -17
- package/app/nodes/page.tsx +62 -49
- package/app/page.tsx +60 -282
- package/app/server-logs/page.tsx +40 -14
- package/app/servers/page.tsx +33 -12
- package/app/settings/networks/page.tsx +17 -15
- package/app/settings/page.tsx +102 -96
- package/app/settings/tokens/page.tsx +5 -5
- package/app/tasks/[id]/page.tsx +10 -10
- package/app/tasks/page.tsx +58 -34
- package/bin/start.js +0 -0
- package/package.json +1 -1
- package/public/favicon.svg +1 -1
- package/public/manifest.webmanifest +24 -0
- package/public/robots.txt +7 -0
- package/.next/server/app/api/hub/license/route/app-paths-manifest.json +0 -3
- package/.next/server/app/api/hub/license/route/build-manifest.json +0 -9
- package/.next/server/app/api/hub/license/route/server-reference-manifest.json +0 -4
- package/.next/server/app/api/hub/license/route.js +0 -7
- package/.next/server/app/api/hub/license/route.js.map +0 -5
- package/.next/server/app/api/hub/license/route.js.nft.json +0 -1
- package/.next/server/app/api/hub/license/route_client-reference-manifest.js +0 -3
- package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_license_route_actions_0a4.fuh.js +0 -3
- package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_license_route_actions_0a4.fuh.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__0rovr5-._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0rovr5-._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0lu1wok._.js +0 -8
- package/.next/server/chunks/ssr/[root-of-the-server]__0lu1wok._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__11fu-5m._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__11fu-5m._.js.map +0 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_057q.ne._.js +0 -3
- package/.next/server/chunks/ssr/agent-network-dashboard_app_057q.ne._.js.map +0 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js +0 -3
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js.map +0 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js +0 -9
- package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js.map +0 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0s5uqlp._.js +0 -9
- package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0s5uqlp._.js.map +0 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_server-logs_page_tsx_0dg.l_8._.js +0 -3
- package/.next/server/chunks/ssr/agent-network-dashboard_app_server-logs_page_tsx_0dg.l_8._.js.map +0 -1
- package/.next/static/chunks/0-mpa_947ipeq.js +0 -1
- package/.next/static/chunks/02to42x11p557.js +0 -7
- package/.next/static/chunks/03~5pxwbxxw-b.js +0 -1
- package/.next/static/chunks/04~fkia6-79k3.js +0 -1
- package/.next/static/chunks/0561vp5-q5.zp.js +0 -1
- package/.next/static/chunks/05uk96gc~9mni.js +0 -1
- package/.next/static/chunks/085rejlait1fs.js +0 -1
- package/.next/static/chunks/0a.9~-nf0gpec.js +0 -1
- package/.next/static/chunks/0im751o4n61c7.js +0 -1
- package/.next/static/chunks/0inql3s9ldyx5.js +0 -1
- package/.next/static/chunks/0ku0fjqlm9mca.js +0 -1
- package/.next/static/chunks/0mcamnu4w_x1r.js +0 -4
- package/.next/static/chunks/0ss8u23bnbyry.js +0 -1
- package/.next/static/chunks/0~rv5y.y5my9s.css +0 -1
- package/.next/static/chunks/13yktdzuatx3d.js +0 -1
- package/.next/static/chunks/15-ltfhot3b4n.js +0 -7
- package/.next/static/chunks/16ls93seuyj8a.js +0 -1
- package/.next/static/chunks/17sxlwlx5fhrp.css +0 -1
- package/.next/static/chunks/181u38qblp8lz.js +0 -1
- package/.next/static/media/4fa387ec64143e14-s.0.qu-9752pffj.woff2 +0 -0
- package/.next/static/media/5ce348bf30bf5439-s.0ee55_hj9qcer.woff2 +0 -0
- package/.next/static/media/6306c77e7c8268e4-s.0mao5jbfbduzp.woff2 +0 -0
- package/.next/static/media/797e433ab948586e-s.p.09zddjkbdep5a.woff2 +0 -0
- package/.next/static/media/7d817b4c03b0c5f1-s.0uzt.a6d44yda.woff2 +0 -0
- package/.next/static/media/bbc41e54d2fcbd21-s.0mvwgmnhv29no.woff2 +0 -0
- package/app/api/hub/license/route.ts +0 -33
- package/app/components/BroadcastBar.tsx +0 -84
- package/app/components/InboxPanel.tsx +0 -36
- /package/.next/static/{vv4Gz5yVhOzydMI2UlT1l → TDZA1Sx9EZPbGBOvEQtJ7}/_buildManifest.js +0 -0
- /package/.next/static/{vv4Gz5yVhOzydMI2UlT1l → TDZA1Sx9EZPbGBOvEQtJ7}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{vv4Gz5yVhOzydMI2UlT1l → TDZA1Sx9EZPbGBOvEQtJ7}/_ssgManifest.js +0 -0
package/app/layout.tsx
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import type { Metadata, Viewport } from "next";
|
|
2
|
-
import { Geist_Mono } from "next/font/google";
|
|
2
|
+
import { Geist, Geist_Mono } from "next/font/google";
|
|
3
3
|
import "./globals.css";
|
|
4
4
|
import { AppShell } from "./components/AppShell";
|
|
5
5
|
import { NetworkProvider } from "./lib/network-context";
|
|
6
6
|
import { ThemeProvider } from "./components/ThemeSwitcher";
|
|
7
7
|
import { PwaInstaller } from "./components/PwaInstaller";
|
|
8
8
|
|
|
9
|
+
// #217 D1 (Vincent: OpenWebUI-style serious-product feel): UI text is
|
|
10
|
+
// sans; mono stays for code blocks, IDs and logs via .font-mono.
|
|
11
|
+
const geistSans = Geist({
|
|
12
|
+
variable: "--font-geist-sans",
|
|
13
|
+
subsets: ["latin"],
|
|
14
|
+
});
|
|
15
|
+
|
|
9
16
|
const geistMono = Geist_Mono({
|
|
10
17
|
variable: "--font-geist-mono",
|
|
11
18
|
subsets: ["latin"],
|
|
@@ -20,7 +27,26 @@ export const metadata: Metadata = {
|
|
|
20
27
|
statusBarStyle: "black-translucent",
|
|
21
28
|
title: "Agent Network",
|
|
22
29
|
},
|
|
23
|
-
|
|
30
|
+
// R26 of #190: Next.js 16 renders `mobile-web-app-capable` (the modern
|
|
31
|
+
// standardized meta) but NOT the legacy `apple-mobile-web-app-capable`.
|
|
32
|
+
// iOS Safari < 16.4 only checks the apple-prefixed name; without it,
|
|
33
|
+
// the dashboard won't install as a web app on those iOS versions and
|
|
34
|
+
// the statusBarStyle + apple-touch-icon (R24) are silently ignored.
|
|
35
|
+
// Emit the legacy alias via metadata.other for back-compat.
|
|
36
|
+
other: {
|
|
37
|
+
'apple-mobile-web-app-capable': 'yes',
|
|
38
|
+
},
|
|
39
|
+
// R24 of #190: appleWebApp.capable above declares this is an iOS web
|
|
40
|
+
// app, but only `icon` was specified, leaving iOS Safari to fall back
|
|
41
|
+
// to a generic icon on Add-to-Home-Screen. iOS 15+ accepts SVG via
|
|
42
|
+
// `rel="apple-touch-icon" type="image/svg+xml"`; older iOS will fall
|
|
43
|
+
// back to the manifest icons (added R23) and then the generic. Reuse
|
|
44
|
+
// the existing sleep2agi-logo.svg — no new asset shipped.
|
|
45
|
+
icons: {
|
|
46
|
+
icon: [{ url: '/favicon.svg', type: 'image/svg+xml' }],
|
|
47
|
+
apple: [{ url: '/sleep2agi-logo.svg', type: 'image/svg+xml' }],
|
|
48
|
+
shortcut: '/favicon.svg',
|
|
49
|
+
},
|
|
24
50
|
openGraph: {
|
|
25
51
|
title: "Agent Network Dashboard",
|
|
26
52
|
description: "Real-time monitoring dashboard for Agent Network nodes",
|
|
@@ -33,7 +59,14 @@ export const viewport: Viewport = {
|
|
|
33
59
|
initialScale: 1,
|
|
34
60
|
maximumScale: 5,
|
|
35
61
|
userScalable: true,
|
|
36
|
-
themeColor: "#
|
|
62
|
+
themeColor: "#0b0b0d",
|
|
63
|
+
// R25 of #190: metadata.appleWebApp.statusBarStyle above is
|
|
64
|
+
// "black-translucent", which renders the iOS status bar as a
|
|
65
|
+
// transparent overlay above the page (rather than reserving its own
|
|
66
|
+
// strip). Without viewportFit: "cover" the resulting safe-area-inset
|
|
67
|
+
// env() values resolve to 0, so the HealthBanner gets occluded by
|
|
68
|
+
// the clock/battery row when the user opens the installed PWA.
|
|
69
|
+
viewportFit: "cover",
|
|
37
70
|
};
|
|
38
71
|
|
|
39
72
|
// Inline pre-paint script to apply persisted theme before React hydrates,
|
|
@@ -51,7 +84,7 @@ export default function RootLayout({
|
|
|
51
84
|
children: React.ReactNode;
|
|
52
85
|
}>) {
|
|
53
86
|
return (
|
|
54
|
-
<html lang="en" data-theme="cyber" className={`${geistMono.variable} h-full antialiased`}>
|
|
87
|
+
<html lang="en" data-theme="cyber" className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}>
|
|
55
88
|
<head>
|
|
56
89
|
<script dangerouslySetInnerHTML={{ __html: themeBootScript }} />
|
|
57
90
|
</head>
|
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/lib/status.ts
CHANGED
|
@@ -23,39 +23,46 @@ export type TaskStatus = typeof TASK_STATUSES[number];
|
|
|
23
23
|
|
|
24
24
|
/** Pill / chip background+text+border. Tailwind classes, not inlined hex,
|
|
25
25
|
* because chips are static class names safe from purge. */
|
|
26
|
+
/** #217 D3 (OpenWebUI-style color restraint): the 9-hue rainbow is
|
|
27
|
+
* collapsed to a semantic triad — green = actively running, red =
|
|
28
|
+
* failed, gray = everything else (in-flight states in lighter gray,
|
|
29
|
+
* terminal states dimmer). Color now means something instead of
|
|
30
|
+
* decorating every enum value. */
|
|
26
31
|
export const STATUS_CHIP_CLASS: Record<string, string> = {
|
|
27
32
|
created: 'bg-gray-500/10 text-gray-400 border-gray-500/20',
|
|
28
|
-
delivered: 'bg-
|
|
29
|
-
acked: 'bg-
|
|
33
|
+
delivered: 'bg-gray-500/10 text-gray-300 border-gray-500/20',
|
|
34
|
+
acked: 'bg-gray-500/10 text-gray-300 border-gray-500/20',
|
|
30
35
|
running: 'bg-green-500/10 text-green-300 border-green-500/20',
|
|
31
|
-
replied: 'bg-
|
|
36
|
+
replied: 'bg-gray-500/10 text-gray-300 border-gray-500/20',
|
|
32
37
|
closed: 'bg-gray-500/10 text-gray-500 border-gray-500/20',
|
|
33
38
|
failed: 'bg-red-500/10 text-red-300 border-red-500/20',
|
|
34
|
-
cancelled: 'bg-
|
|
35
|
-
expired: 'bg-
|
|
39
|
+
cancelled: 'bg-gray-500/10 text-gray-500 border-gray-500/20',
|
|
40
|
+
expired: 'bg-gray-500/10 text-gray-500 border-gray-500/20',
|
|
36
41
|
};
|
|
37
42
|
|
|
38
43
|
/** Inline hex dots — used wherever Tailwind would purge dynamic class
|
|
39
44
|
* names (`bg-${family}-400`). Style attribute carries the color. */
|
|
40
45
|
export const STATUS_DOT_HEX: Record<string, string> = {
|
|
41
46
|
created: '#9ca3af',
|
|
42
|
-
delivered: '#
|
|
43
|
-
acked: '#
|
|
47
|
+
delivered: '#9ca3af',
|
|
48
|
+
acked: '#9ca3af',
|
|
44
49
|
running: '#4ade80',
|
|
45
|
-
replied: '#
|
|
50
|
+
replied: '#9ca3af',
|
|
46
51
|
closed: '#6b7280',
|
|
47
52
|
failed: '#f87171',
|
|
48
|
-
cancelled: '#
|
|
49
|
-
expired: '#
|
|
53
|
+
cancelled: '#6b7280',
|
|
54
|
+
expired: '#6b7280',
|
|
50
55
|
};
|
|
51
56
|
|
|
52
57
|
/** Session lifecycle (distinct from task lifecycle above). Shared by
|
|
53
58
|
* /nodes status pills and /admin Online Sessions row chips so an
|
|
54
59
|
* agent in `blocked` state reads the same color everywhere.
|
|
55
60
|
* Round 91 — extracted from app/nodes/page.tsx. */
|
|
61
|
+
/** D3: idle drops from blue to gray — only working (green), blocked
|
|
62
|
+
* (amber, actionable) and error (red) earn color. */
|
|
56
63
|
export const SESSION_STATUS_CHIP_CLASS: Record<string, string> = {
|
|
57
64
|
working: 'bg-green-500/10 text-green-300 border-green-500/20',
|
|
58
|
-
idle: 'bg-
|
|
65
|
+
idle: 'bg-gray-500/10 text-gray-300 border-gray-500/20',
|
|
59
66
|
blocked: 'bg-yellow-500/10 text-yellow-300 border-yellow-500/20',
|
|
60
67
|
error: 'bg-red-500/10 text-red-300 border-red-500/20',
|
|
61
68
|
offline: 'bg-gray-500/10 text-gray-500 border-gray-500/20',
|
|
@@ -65,7 +72,7 @@ export const SESSION_STATUS_CHIP_CLASS: Record<string, string> = {
|
|
|
65
72
|
* would be too heavy — e.g. /node detail header status label. */
|
|
66
73
|
export const SESSION_STATUS_TEXT_CLASS: Record<string, string> = {
|
|
67
74
|
working: 'text-green-400',
|
|
68
|
-
idle: 'text-
|
|
75
|
+
idle: 'text-gray-400',
|
|
69
76
|
blocked: 'text-yellow-400',
|
|
70
77
|
error: 'text-red-400',
|
|
71
78
|
offline: 'text-gray-500',
|
|
@@ -74,12 +81,12 @@ export const SESSION_STATUS_TEXT_CLASS: Record<string, string> = {
|
|
|
74
81
|
/** Solid bar segment background for the Tasks distribution bar. */
|
|
75
82
|
export const STATUS_BAR_CLASS: Record<string, string> = {
|
|
76
83
|
created: 'bg-gray-500',
|
|
77
|
-
delivered: 'bg-
|
|
78
|
-
acked: 'bg-
|
|
84
|
+
delivered: 'bg-gray-400',
|
|
85
|
+
acked: 'bg-gray-300',
|
|
79
86
|
running: 'bg-green-500',
|
|
80
|
-
replied: 'bg-
|
|
87
|
+
replied: 'bg-gray-400',
|
|
81
88
|
closed: 'bg-gray-600',
|
|
82
89
|
failed: 'bg-red-500',
|
|
83
|
-
cancelled: 'bg-
|
|
84
|
-
expired: 'bg-
|
|
90
|
+
cancelled: 'bg-gray-600',
|
|
91
|
+
expired: 'bg-gray-600',
|
|
85
92
|
};
|
package/app/login/page.tsx
CHANGED
|
@@ -46,7 +46,7 @@ export default function LoginPage() {
|
|
|
46
46
|
};
|
|
47
47
|
|
|
48
48
|
return (
|
|
49
|
-
<main className="min-h-screen bg-[#
|
|
49
|
+
<main className="min-h-screen bg-[#0b0b0d] text-gray-100 flex items-center justify-center relative overflow-hidden px-4 py-10 sm:py-16">
|
|
50
50
|
{/* Subtle off-grid background — restraint over AI-glow.
|
|
51
51
|
Dark themes get a faint radial wash; light themes show a low-contrast
|
|
52
52
|
dotted grid for surface texture without noise. */}
|
|
@@ -57,7 +57,7 @@ export default function LoginPage() {
|
|
|
57
57
|
{/* Brand mark — uses the favicon 3-node mesh SVG which IS the
|
|
58
58
|
agent-network concept (nodes + edges). Replaces the previous
|
|
59
59
|
flat emerald square placeholder. */}
|
|
60
|
-
<div className="inline-flex items-center justify-center w-16 h-16 mb-4 rounded-2xl bg-[#
|
|
60
|
+
<div className="inline-flex items-center justify-center w-16 h-16 mb-4 rounded-2xl bg-[#161618] border border-[#26262b] anet-login-mark">
|
|
61
61
|
<svg className="w-9 h-9" viewBox="0 0 32 32" aria-hidden>
|
|
62
62
|
<circle cx="16" cy="16" r="10" fill="none" stroke="currentColor" strokeWidth="1.5" opacity="0.25" className="text-cyan-400 anet-login-mark-ring" />
|
|
63
63
|
<line x1="16" y1="10" x2="10" y2="20" stroke="currentColor" strokeWidth="1" opacity="0.5" className="text-cyan-400 anet-login-mark-edge" />
|
|
@@ -77,7 +77,7 @@ export default function LoginPage() {
|
|
|
77
77
|
</div>
|
|
78
78
|
|
|
79
79
|
{/* Mode toggle */}
|
|
80
|
-
<div className="flex rounded-lg border border-[#
|
|
80
|
+
<div className="flex rounded-lg border border-[#26262b] bg-[#161618] p-1 mb-4">
|
|
81
81
|
{(['login', 'register'] as const).map(m => (
|
|
82
82
|
<button key={m} type="button" onClick={() => { setMode(m); setError(''); }}
|
|
83
83
|
className={`flex-1 rounded-md px-3 py-2 text-sm transition-colors ${mode === m ? 'bg-cyan-500/10 text-cyan-300' : 'text-gray-500 hover:text-gray-200'}`}>
|
|
@@ -86,13 +86,13 @@ export default function LoginPage() {
|
|
|
86
86
|
))}
|
|
87
87
|
</div>
|
|
88
88
|
|
|
89
|
-
<form onSubmit={submit} className="border border-[#
|
|
89
|
+
<form onSubmit={submit} className="border border-[#26262b] bg-[#161618]/80 backdrop-blur-sm rounded-xl p-6 shadow-2xl shadow-black/30">
|
|
90
90
|
<label htmlFor="username" className="block text-xs text-gray-500 mb-2 uppercase tracking-wider">
|
|
91
91
|
Username
|
|
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-[#
|
|
95
|
+
className="w-full bg-[#0e0e10] border border-[#26262b] 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-[#
|
|
103
|
+
className="w-full bg-[#0e0e10] border border-[#26262b] 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
|
@@ -75,8 +75,10 @@ export default function LogsPage() {
|
|
|
75
75
|
}, [fetchLogs]);
|
|
76
76
|
|
|
77
77
|
return (
|
|
78
|
-
<div className="min-h-screen bg-[#
|
|
79
|
-
|
|
78
|
+
<div className="min-h-screen bg-[#0b0b0d] text-gray-100 p-4 sm:p-6">
|
|
79
|
+
{/* #209 R36: mobile vertical rhythm — same R28/R30/R31 pattern,
|
|
80
|
+
mb-6 → mb-4 sm:mb-6 here and on the action-filter strip below. */}
|
|
81
|
+
<div className="flex items-center gap-4 mb-4 sm:mb-6">
|
|
80
82
|
<h1 className="text-2xl font-bold text-white lg:ml-0 ml-10">Audit Log</h1>
|
|
81
83
|
{/* Round 87: dropped {logs.length} header chip — r43 added
|
|
82
84
|
`All <count>` to the action-filter strip below, which
|
|
@@ -87,7 +89,7 @@ export default function LogsPage() {
|
|
|
87
89
|
status tabs / Overview agent filter. Each chip carries its
|
|
88
90
|
ACTION_STRIPE dot so the visual key matches the per-row left
|
|
89
91
|
rail. Counts come from the currently loaded log set. */}
|
|
90
|
-
<div className="mb-6 flex flex-wrap items-center gap-1 anet-tabstrip-wrap overflow-x-auto sm:overflow-x-visible">
|
|
92
|
+
<div className="mb-4 sm:mb-6 flex flex-wrap items-center gap-1 anet-tabstrip-wrap overflow-x-auto sm:overflow-x-visible">
|
|
91
93
|
{(() => {
|
|
92
94
|
const counts: Record<string, number> = {};
|
|
93
95
|
logs.forEach(l => { counts[l.action] = (counts[l.action] || 0) + 1; });
|
|
@@ -111,7 +113,7 @@ export default function LogsPage() {
|
|
|
111
113
|
className={`flex items-center gap-1.5 rounded-md px-2.5 py-1 text-xs transition-colors disabled:opacity-30 disabled:cursor-not-allowed shrink-0 whitespace-nowrap ${
|
|
112
114
|
isActive
|
|
113
115
|
? 'bg-cyan-500/10 text-cyan-300 border border-cyan-500/30'
|
|
114
|
-
: 'text-gray-500 hover:text-gray-200 hover:bg-[#
|
|
116
|
+
: 'text-gray-500 hover:text-gray-200 hover:bg-[#1c1c1f] border border-transparent'
|
|
115
117
|
}`}
|
|
116
118
|
>
|
|
117
119
|
{c.dot && <span aria-hidden className="inline-block w-1.5 h-1.5 rounded-full" style={{ backgroundColor: c.dot }} />}
|
|
@@ -162,11 +164,15 @@ export default function LogsPage() {
|
|
|
162
164
|
) : logs.length === 0 ? (
|
|
163
165
|
<EmptyState variant="logs" />
|
|
164
166
|
) : (
|
|
165
|
-
|
|
167
|
+
/* R18 of #190 mobile polish: /logs was 28,791 px on 390 px
|
|
168
|
+
mobile; same dense-card pattern that R7/R8 hit on /nodes
|
|
169
|
+
and /tasks. Tighten card padding and inter-card gap on
|
|
170
|
+
mobile, sm:-gated so desktop stays comfortable. */
|
|
171
|
+
<div className="space-y-1 sm:space-y-2">
|
|
166
172
|
{logs.map(log => {
|
|
167
173
|
const userName = log.username || log.user_id;
|
|
168
174
|
return (
|
|
169
|
-
<div key={log.id} className="relative bg-[#
|
|
175
|
+
<div key={log.id} className="relative bg-[#161618] border border-[#26262b] rounded-lg pl-3 pr-3 py-2 sm:pl-4 sm:pr-4 sm:py-3 hover:border-[#3a3a41] transition-colors overflow-hidden">
|
|
170
176
|
{/* 2px left rail per action (round 33) — failed logins, token
|
|
171
177
|
rotations spike out of a wall of register/login rows. */}
|
|
172
178
|
<span
|
package/app/manifest.ts
CHANGED
|
@@ -8,8 +8,8 @@ export default function manifest(): MetadataRoute.Manifest {
|
|
|
8
8
|
start_url: '/',
|
|
9
9
|
scope: '/',
|
|
10
10
|
display: 'standalone',
|
|
11
|
-
background_color: '#
|
|
12
|
-
theme_color: '#
|
|
11
|
+
background_color: '#0b0b0d',
|
|
12
|
+
theme_color: '#0b0b0d',
|
|
13
13
|
orientation: 'portrait',
|
|
14
14
|
icons: [
|
|
15
15
|
{
|
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>();
|
|
@@ -104,56 +113,64 @@ export default function MessagesPage() {
|
|
|
104
113
|
}, [filtered]);
|
|
105
114
|
|
|
106
115
|
return (
|
|
107
|
-
<div className="min-h-screen bg-[#
|
|
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"
|
|
116
|
+
<div className="min-h-screen bg-[#0b0b0d] text-gray-100 p-4 sm:p-6">
|
|
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-[#
|
|
173
|
+
className="bg-[#161618] border border-[#26262b] 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>
|
|
@@ -161,7 +178,7 @@ export default function MessagesPage() {
|
|
|
161
178
|
<option value="broadcast">broadcast</option>
|
|
162
179
|
<option value="reply">reply</option>
|
|
163
180
|
</select>
|
|
164
|
-
<div className="flex rounded-lg border border-[#
|
|
181
|
+
<div className="flex rounded-lg border border-[#26262b] bg-[#161618] p-0.5">
|
|
165
182
|
{(['timeline', 'grouped'] as const).map(mode => (
|
|
166
183
|
<button key={mode} type="button" onClick={() => setViewMode(mode)}
|
|
167
184
|
className={`rounded-md px-2.5 py-1.5 text-xs transition-colors ${viewMode === mode ? 'bg-cyan-500/10 text-cyan-300' : 'text-gray-500 hover:text-gray-200'}`}>
|
|
@@ -180,17 +197,20 @@ export default function MessagesPage() {
|
|
|
180
197
|
</div>
|
|
181
198
|
)}
|
|
182
199
|
|
|
200
|
+
{/* #217 S8 (less is more): on phones the sender chips wrapped into
|
|
201
|
+
~5 rows and pushed the first message below the fold. One
|
|
202
|
+
scrollable row on mobile; wrap returns from sm: up. */}
|
|
183
203
|
{quickFromChips.length > 0 && (
|
|
184
|
-
<div className="mb-6 flex flex-wrap
|
|
204
|
+
<div className="mb-4 sm:mb-6 flex gap-2 overflow-x-auto whitespace-nowrap pb-1 scrollbar-thin sm:flex-wrap sm:overflow-visible sm:whitespace-normal sm:pb-0">
|
|
185
205
|
{quickFromChips.map(alias => (
|
|
186
206
|
<button
|
|
187
207
|
key={alias}
|
|
188
208
|
type="button"
|
|
189
209
|
onClick={() => setSearch(`from:${alias}`)}
|
|
190
|
-
className={`rounded-full border px-3 py-1 text-xs transition-colors ${
|
|
210
|
+
className={`shrink-0 rounded-full border px-3 py-1 text-xs transition-colors ${
|
|
191
211
|
search === `from:${alias}`
|
|
192
212
|
? 'border-cyan-500/30 bg-cyan-500/10 text-cyan-300'
|
|
193
|
-
: 'border-[#
|
|
213
|
+
: 'border-[#26262b] bg-[#161618] text-gray-400 hover:text-gray-200'
|
|
194
214
|
}`}
|
|
195
215
|
>
|
|
196
216
|
from:{alias}
|
|
@@ -200,7 +220,7 @@ export default function MessagesPage() {
|
|
|
200
220
|
<button
|
|
201
221
|
type="button"
|
|
202
222
|
onClick={() => setSearch('')}
|
|
203
|
-
className="rounded-full border border-gray-700 px-3 py-1 text-xs text-gray-500 hover:text-gray-200"
|
|
223
|
+
className="shrink-0 rounded-full border border-gray-700 px-3 py-1 text-xs text-gray-500 hover:text-gray-200"
|
|
204
224
|
>
|
|
205
225
|
Clear
|
|
206
226
|
</button>
|
|
@@ -217,8 +237,8 @@ export default function MessagesPage() {
|
|
|
217
237
|
) : viewMode === 'grouped' ? (
|
|
218
238
|
<div className="space-y-4">
|
|
219
239
|
{conversations.map((conv, ci) => (
|
|
220
|
-
<div key={ci} className="bg-[#
|
|
221
|
-
<div className="px-4 py-2.5 border-b border-[#
|
|
240
|
+
<div key={ci} className="bg-[#161618] border border-[#26262b] rounded-xl overflow-hidden">
|
|
241
|
+
<div className="px-4 py-2.5 border-b border-[#26262b] flex items-center justify-between">
|
|
222
242
|
<div className="flex items-center gap-2">
|
|
223
243
|
<AliasAvatar alias={conv.participants[0]} size={16} />
|
|
224
244
|
<span className="text-sm text-gray-200 font-medium">{conv.participants[0]}</span>
|
|
@@ -266,9 +286,9 @@ export default function MessagesPage() {
|
|
|
266
286
|
Halve it on mobile. */}
|
|
267
287
|
{gapExceeded && (
|
|
268
288
|
<div className="my-2 sm:my-4 flex items-center gap-3">
|
|
269
|
-
<div className="h-px flex-1 bg-[#
|
|
289
|
+
<div className="h-px flex-1 bg-[#26262b]" />
|
|
270
290
|
<div className="text-[11px] text-gray-600">{formatDividerLabel(message.created_at)}</div>
|
|
271
|
-
<div className="h-px flex-1 bg-[#
|
|
291
|
+
<div className="h-px flex-1 bg-[#26262b]" />
|
|
272
292
|
</div>
|
|
273
293
|
)}
|
|
274
294
|
|
package/app/node/page.tsx
CHANGED
|
@@ -60,7 +60,7 @@ function TmuxViewer({ tmuxName }: { tmuxName: string }) {
|
|
|
60
60
|
};
|
|
61
61
|
|
|
62
62
|
return (
|
|
63
|
-
<div className="bg-[#
|
|
63
|
+
<div className="bg-[#161618] border border-[#26262b] rounded-xl p-4">
|
|
64
64
|
<div className="flex items-center justify-between mb-2">
|
|
65
65
|
<h2 className="text-sm font-semibold text-gray-300">Terminal ({tmuxName})</h2>
|
|
66
66
|
<div className="flex gap-2">
|
|
@@ -76,7 +76,7 @@ function TmuxViewer({ tmuxName }: { tmuxName: string }) {
|
|
|
76
76
|
</div>
|
|
77
77
|
</div>
|
|
78
78
|
{output && (
|
|
79
|
-
<pre className={`text-[11px] text-green-300 bg-[#
|
|
79
|
+
<pre className={`text-[11px] text-green-300 bg-[#060607] rounded-lg px-3 py-2 border border-[#1c1c1f] overflow-x-auto whitespace-pre font-mono ${expanded ? 'max-h-96' : 'max-h-32'} overflow-y-auto`}>
|
|
80
80
|
{output}
|
|
81
81
|
</pre>
|
|
82
82
|
)}
|
|
@@ -142,7 +142,7 @@ function NodeDetailContent() {
|
|
|
142
142
|
|
|
143
143
|
if (!alias) {
|
|
144
144
|
return (
|
|
145
|
-
<div className="min-h-screen bg-[#
|
|
145
|
+
<div className="min-h-screen bg-[#0b0b0d] text-gray-100 p-6 flex items-center justify-center">
|
|
146
146
|
<p className="text-gray-500">No alias specified. <Link href="/" className="text-blue-400 hover:underline">Back to dashboard</Link></p>
|
|
147
147
|
</div>
|
|
148
148
|
);
|
|
@@ -153,7 +153,7 @@ function NodeDetailContent() {
|
|
|
153
153
|
const statusColor = SESSION_STATUS_TEXT_CLASS[session?.status || ''] || SESSION_STATUS_TEXT_CLASS.offline;
|
|
154
154
|
|
|
155
155
|
return (
|
|
156
|
-
<div className="min-h-screen bg-[#
|
|
156
|
+
<div className="min-h-screen bg-[#0b0b0d] text-gray-100 p-4 sm:p-6">
|
|
157
157
|
{/* Header — round 40: avatar joins the alias for cross-page hue
|
|
158
158
|
consistency; agent type lives in a subtitle so users know what
|
|
159
159
|
kind of node they're looking at. */}
|
|
@@ -203,10 +203,10 @@ function NodeFullPanel({ alias, session, sse, sendMsg, setSendMsg, sending, send
|
|
|
203
203
|
}, [tab, eventsLoaded]);
|
|
204
204
|
|
|
205
205
|
return (
|
|
206
|
-
<div className="bg-[#
|
|
206
|
+
<div className="bg-[#111113] border border-[#26262b] rounded-xl overflow-hidden h-[calc(100vh-140px)] flex flex-col">
|
|
207
207
|
{/* Tab bar — round 40: emoji icons (💬 📋 📊) replaced with stroke
|
|
208
208
|
SVG to drop the "AI generated" tell. */}
|
|
209
|
-
<div className="flex border-b border-[#
|
|
209
|
+
<div className="flex border-b border-[#26262b] bg-[#0e0e10] shrink-0">
|
|
210
210
|
{([
|
|
211
211
|
{ id: 'chat', label: 'Chat', 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' },
|
|
212
212
|
{ id: 'events', label: 'Events', 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' },
|
|
@@ -241,7 +241,7 @@ function NodeFullPanel({ alias, session, sse, sendMsg, setSendMsg, sending, send
|
|
|
241
241
|
|
|
242
242
|
{tab === 'info' && (
|
|
243
243
|
<div className="p-4 overflow-y-auto h-full space-y-4 max-w-2xl">
|
|
244
|
-
<div className="bg-[#
|
|
244
|
+
<div className="bg-[#161618] border border-[#26262b] rounded-xl p-4">
|
|
245
245
|
<h2 className="text-sm font-semibold text-gray-300 mb-3">Node Info</h2>
|
|
246
246
|
<div className="space-y-2 text-xs">
|
|
247
247
|
{[
|
|
@@ -278,7 +278,7 @@ function NodeFullPanel({ alias, session, sse, sendMsg, setSendMsg, sending, send
|
|
|
278
278
|
|
|
279
279
|
{/* Current Task */}
|
|
280
280
|
{session?.task && (
|
|
281
|
-
<div className="bg-[#
|
|
281
|
+
<div className="bg-[#161618] border border-[#26262b] rounded-xl p-4">
|
|
282
282
|
<h2 className="text-sm font-semibold text-gray-300 mb-2">Current Task</h2>
|
|
283
283
|
<p className="text-xs text-gray-400 whitespace-pre-wrap">{session.task}</p>
|
|
284
284
|
</div>
|
|
@@ -286,14 +286,14 @@ function NodeFullPanel({ alias, session, sse, sendMsg, setSendMsg, sending, send
|
|
|
286
286
|
|
|
287
287
|
{/* Output */}
|
|
288
288
|
{session?.output && (
|
|
289
|
-
<div className="bg-[#
|
|
289
|
+
<div className="bg-[#161618] border border-[#26262b] rounded-xl p-4">
|
|
290
290
|
<h2 className="text-sm font-semibold text-gray-300 mb-2">Last Output</h2>
|
|
291
291
|
<p className="text-xs text-gray-400 whitespace-pre-wrap max-h-48 overflow-y-auto">{session.output}</p>
|
|
292
292
|
</div>
|
|
293
293
|
)}
|
|
294
294
|
|
|
295
295
|
{/* Send Task */}
|
|
296
|
-
<div className="bg-[#
|
|
296
|
+
<div className="bg-[#161618] border border-[#26262b] rounded-xl p-4">
|
|
297
297
|
<h2 className="text-sm font-semibold text-gray-300 mb-2">Send Task</h2>
|
|
298
298
|
<div className="flex gap-2">
|
|
299
299
|
<input
|
|
@@ -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-[#
|
|
305
|
+
className="flex-1 bg-[#0e0e10] border border-[#26262b] 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>
|
|
@@ -354,11 +354,11 @@ function EventsTimeline({ events, loading }: { events: Array<{ id: number; event
|
|
|
354
354
|
return (
|
|
355
355
|
<div className="p-4 overflow-y-auto h-full space-y-4">
|
|
356
356
|
{[...groups.entries()].map(([taskId, taskEvents]) => (
|
|
357
|
-
<div key={taskId} className="bg-[#
|
|
357
|
+
<div key={taskId} className="bg-[#161618] border border-[#26262b] rounded-xl p-4">
|
|
358
358
|
<div className="text-[10px] text-gray-600 mb-3 truncate">Task: {taskId.slice(0, 20)}...</div>
|
|
359
359
|
<div className="relative pl-6">
|
|
360
360
|
{/* Vertical line */}
|
|
361
|
-
<div className="absolute left-[9px] top-2 bottom-2 w-0.5 bg-[#
|
|
361
|
+
<div className="absolute left-[9px] top-2 bottom-2 w-0.5 bg-[#1c1c1f]" />
|
|
362
362
|
|
|
363
363
|
{taskEvents.map((e, i) => {
|
|
364
364
|
const cfg = EVENT_COLORS[e.to_status] || EVENT_COLORS.created;
|
|
@@ -366,8 +366,8 @@ function EventsTimeline({ events, loading }: { events: Array<{ id: number; event
|
|
|
366
366
|
<div key={e.id} className="relative pb-4 last:pb-0">
|
|
367
367
|
{/* Dot on line — round 59: emoji icons replaced with
|
|
368
368
|
a 10px stroke SVG path matching event type. */}
|
|
369
|
-
<div className={`absolute -left-6 top-0.5 w-[18px] h-[18px] rounded-full ${cfg.dot} flex items-center justify-center z-10 border-2 border-[#
|
|
370
|
-
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="#
|
|
369
|
+
<div className={`absolute -left-6 top-0.5 w-[18px] h-[18px] rounded-full ${cfg.dot} flex items-center justify-center z-10 border-2 border-[#161618]`}>
|
|
370
|
+
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="#0e0e10" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden>
|
|
371
371
|
<path d={cfg.path} />
|
|
372
372
|
</svg>
|
|
373
373
|
</div>
|
|
@@ -398,7 +398,17 @@ function EventsTimeline({ events, loading }: { events: Array<{ id: number; event
|
|
|
398
398
|
|
|
399
399
|
export default function NodePage() {
|
|
400
400
|
return (
|
|
401
|
-
<Suspense fallback={
|
|
401
|
+
<Suspense fallback={
|
|
402
|
+
// #209 R36: same pattern R30 fixed on /tasks — bare "Loading..."
|
|
403
|
+
// string used p-6 (no mobile padding tighten) + no left indent,
|
|
404
|
+
// so the fixed top-3 left-3 mobile hamburger covered the "Loa".
|
|
405
|
+
// Match the loaded layout: p-4 sm:p-6 + lg:ml-0 ml-10 mobile
|
|
406
|
+
// indent on the text. Loading state now visually maps to the
|
|
407
|
+
// populated /node detail page.
|
|
408
|
+
<div className="min-h-screen bg-[#0b0b0d] text-gray-100 p-4 sm:p-6">
|
|
409
|
+
<div className="lg:ml-0 ml-10 text-gray-500 text-sm">Loading…</div>
|
|
410
|
+
</div>
|
|
411
|
+
}>
|
|
402
412
|
<NodeDetailContent />
|
|
403
413
|
</Suspense>
|
|
404
414
|
);
|