@sleep2agi/agent-network-dashboard 0.4.6-preview.3 → 0.4.6-preview.31
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 +1 -0
- package/.next/build/chunks/{node_modules_13sb.px._.js → 00jm_02mlcew._.js} +167 -167
- package/.next/build/chunks/00jm_02mlcew._.js.map +47 -0
- package/.next/build/chunks/{[root-of-the-server]__0ubbtyl._.js → [root-of-the-server]__0tgc-8p._.js} +3 -3
- package/.next/build/chunks/{[root-of-the-server]__0d-m0h0._.js → [root-of-the-server]__13z64y.._.js} +7 -7
- package/.next/build/chunks/{[root-of-the-server]__0d-m0h0._.js.map → [root-of-the-server]__13z64y.._.js.map} +2 -2
- package/.next/build/chunks/[turbopack-node]_transforms_postcss_ts_0r4x5ys._.js +13 -0
- package/.next/build/chunks/[turbopack]_runtime.js +1 -1
- package/.next/build/postcss.js +4 -4
- package/.next/build-manifest.json +8 -8
- package/.next/diagnostics/route-bundle-stats.json +157 -143
- package/.next/fallback-build-manifest.json +3 -3
- package/.next/next-minimal-server.js.nft.json +1 -1
- package/.next/next-server.js.nft.json +1 -1
- package/.next/prerender-manifest.json +3 -3
- package/.next/required-server-files.js +4 -4
- package/.next/required-server-files.json +4 -4
- package/.next/routes-manifest.json +6 -0
- package/.next/server/app/_global-error/page/build-manifest.json +5 -5
- package/.next/server/app/_global-error/page.js +9 -9
- package/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +8 -8
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/_global-error.segments/_full.segment.rsc +8 -8
- package/.next/server/app/_global-error.segments/_head.segment.rsc +4 -4
- package/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page/build-manifest.json +5 -5
- package/.next/server/app/_not-found/page/next-font-manifest.json +1 -1
- package/.next/server/app/_not-found/page.js +12 -12
- 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 +2 -2
- package/.next/server/app/_not-found.rsc +12 -12
- package/.next/server/app/_not-found.segments/_full.segment.rsc +12 -12
- package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/server/app/_not-found.segments/_index.segment.rsc +7 -7
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/admin/page/build-manifest.json +5 -5
- package/.next/server/app/admin/page/next-font-manifest.json +1 -1
- package/.next/server/app/admin/page.js +13 -13
- 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 +2 -2
- package/.next/server/app/admin.rsc +14 -14
- package/.next/server/app/admin.segments/_full.segment.rsc +14 -14
- package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
- package/.next/server/app/admin.segments/_index.segment.rsc +7 -7
- package/.next/server/app/admin.segments/_tree.segment.rsc +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/api/anet/config/route.js +6 -6
- package/.next/server/app/api/anet/config/route.js.nft.json +1 -1
- package/.next/server/app/api/auth/login/route.js +7 -7
- package/.next/server/app/api/auth/login/route.js.nft.json +1 -1
- package/.next/server/app/api/auth/logout/route.js +7 -7
- package/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
- package/.next/server/app/api/auth/v3/route.js +7 -7
- package/.next/server/app/api/auth/v3/route.js.nft.json +1 -1
- package/.next/server/app/api/hub/audit-log/route.js +6 -6
- package/.next/server/app/api/hub/audit-log/route.js.nft.json +1 -1
- package/.next/server/app/api/hub/auth/route.js +7 -7
- package/.next/server/app/api/hub/auth/route.js.nft.json +1 -1
- package/.next/server/app/api/hub/broadcast/route.js +6 -6
- package/.next/server/app/api/hub/broadcast/route.js.nft.json +1 -1
- package/.next/server/app/api/hub/events/route.js +6 -6
- package/.next/server/app/api/hub/events/route.js.nft.json +1 -1
- package/.next/server/app/api/hub/health/route.js +6 -6
- package/.next/server/app/api/hub/health/route.js.nft.json +1 -1
- package/.next/server/app/api/hub/inbox/route.js +6 -6
- package/.next/server/app/api/hub/inbox/route.js.nft.json +1 -1
- package/.next/server/app/api/hub/license/route.js +6 -6
- package/.next/server/app/api/hub/license/route.js.nft.json +1 -1
- package/.next/server/app/api/hub/messages/route.js +6 -6
- package/.next/server/app/api/hub/messages/route.js.nft.json +1 -1
- package/.next/server/app/api/hub/networks/route.js +6 -6
- package/.next/server/app/api/hub/networks/route.js.nft.json +1 -1
- package/.next/server/app/api/hub/nodes/route.js +6 -6
- package/.next/server/app/api/hub/nodes/route.js.nft.json +1 -1
- package/.next/server/app/api/hub/register/route.js +6 -6
- package/.next/server/app/api/hub/register/route.js.nft.json +1 -1
- package/.next/server/app/api/hub/send/route.js +6 -6
- package/.next/server/app/api/hub/send/route.js.nft.json +1 -1
- package/.next/server/app/api/hub/server-logs/route.js +6 -6
- package/.next/server/app/api/hub/server-logs/route.js.nft.json +1 -1
- package/.next/server/app/api/hub/servers/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/hub/servers/route/build-manifest.json +9 -0
- package/.next/server/app/api/hub/servers/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/hub/servers/route.js +7 -0
- package/.next/server/app/api/hub/servers/route.js.map +5 -0
- package/.next/server/app/api/hub/servers/route.js.nft.json +1 -0
- package/.next/server/app/api/hub/servers/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/hub/session/route.js +6 -6
- package/.next/server/app/api/hub/session/route.js.nft.json +1 -1
- package/.next/server/app/api/hub/stats/route.js +6 -6
- package/.next/server/app/api/hub/stats/route.js.nft.json +1 -1
- package/.next/server/app/api/hub/status/route.js +6 -6
- package/.next/server/app/api/hub/status/route.js.nft.json +1 -1
- package/.next/server/app/api/hub/task-events/route.js +6 -6
- package/.next/server/app/api/hub/task-events/route.js.nft.json +1 -1
- package/.next/server/app/api/hub/tasks/route.js +6 -6
- package/.next/server/app/api/hub/tasks/route.js.nft.json +1 -1
- package/.next/server/app/api/hub/tmux/route.js +6 -6
- package/.next/server/app/api/hub/tmux/route.js.nft.json +1 -1
- package/.next/server/app/api/hub/tokens/route.js +6 -6
- package/.next/server/app/api/hub/tokens/route.js.nft.json +1 -1
- package/.next/server/app/favicon.ico/route.js +6 -6
- package/.next/server/app/favicon.ico/route.js.nft.json +1 -1
- package/.next/server/app/index.html +2 -2
- package/.next/server/app/index.rsc +14 -14
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/index.segments/_full.segment.rsc +14 -14
- package/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/server/app/index.segments/_index.segment.rsc +7 -7
- package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login/page/build-manifest.json +5 -5
- package/.next/server/app/login/page/next-font-manifest.json +1 -1
- package/.next/server/app/login/page.js +13 -13
- 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 +14 -14
- package/.next/server/app/login.segments/_full.segment.rsc +14 -14
- package/.next/server/app/login.segments/_head.segment.rsc +4 -4
- package/.next/server/app/login.segments/_index.segment.rsc +7 -7
- package/.next/server/app/login.segments/_tree.segment.rsc +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/build-manifest.json +5 -5
- package/.next/server/app/logs/page/next-font-manifest.json +1 -1
- package/.next/server/app/logs/page.js +13 -13
- 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 +2 -2
- package/.next/server/app/logs.rsc +14 -14
- package/.next/server/app/logs.segments/_full.segment.rsc +14 -14
- package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
- package/.next/server/app/logs.segments/_index.segment.rsc +7 -7
- package/.next/server/app/logs.segments/_tree.segment.rsc +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/build-manifest.json +5 -5
- package/.next/server/app/messages/page/next-font-manifest.json +1 -1
- package/.next/server/app/messages/page.js +13 -13
- 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 +2 -2
- package/.next/server/app/messages.rsc +14 -14
- package/.next/server/app/messages.segments/_full.segment.rsc +14 -14
- package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
- package/.next/server/app/messages.segments/_index.segment.rsc +7 -7
- package/.next/server/app/messages.segments/_tree.segment.rsc +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/build-manifest.json +5 -5
- package/.next/server/app/node/page/next-font-manifest.json +1 -1
- package/.next/server/app/node/page.js +13 -13
- 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 +2 -2
- package/.next/server/app/node.rsc +14 -14
- package/.next/server/app/node.segments/_full.segment.rsc +14 -14
- package/.next/server/app/node.segments/_head.segment.rsc +4 -4
- package/.next/server/app/node.segments/_index.segment.rsc +7 -7
- package/.next/server/app/node.segments/_tree.segment.rsc +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/build-manifest.json +5 -5
- package/.next/server/app/nodes/page/next-font-manifest.json +1 -1
- package/.next/server/app/nodes/page.js +13 -13
- 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 +2 -2
- package/.next/server/app/nodes.rsc +14 -14
- package/.next/server/app/nodes.segments/_full.segment.rsc +14 -14
- package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
- package/.next/server/app/nodes.segments/_index.segment.rsc +7 -7
- package/.next/server/app/nodes.segments/_tree.segment.rsc +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/build-manifest.json +5 -5
- package/.next/server/app/page/next-font-manifest.json +1 -1
- package/.next/server/app/page.js +13 -13
- 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/build-manifest.json +5 -5
- package/.next/server/app/server-logs/page/next-font-manifest.json +1 -1
- package/.next/server/app/server-logs/page.js +13 -13
- 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 +2 -2
- package/.next/server/app/server-logs.rsc +14 -14
- package/.next/server/app/server-logs.segments/_full.segment.rsc +14 -14
- package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
- package/.next/server/app/server-logs.segments/_index.segment.rsc +7 -7
- package/.next/server/app/server-logs.segments/_tree.segment.rsc +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/settings/networks/page/build-manifest.json +5 -5
- package/.next/server/app/settings/networks/page/next-font-manifest.json +1 -1
- package/.next/server/app/settings/networks/page.js +13 -13
- 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 +2 -2
- package/.next/server/app/settings/networks.rsc +14 -14
- package/.next/server/app/settings/networks.segments/_full.segment.rsc +14 -14
- package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings/networks.segments/_index.segment.rsc +7 -7
- package/.next/server/app/settings/networks.segments/_tree.segment.rsc +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/build-manifest.json +5 -5
- package/.next/server/app/settings/page/next-font-manifest.json +1 -1
- package/.next/server/app/settings/page.js +13 -13
- 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/build-manifest.json +5 -5
- package/.next/server/app/settings/tokens/page/next-font-manifest.json +1 -1
- package/.next/server/app/settings/tokens/page.js +13 -13
- 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 +2 -2
- package/.next/server/app/settings/tokens.rsc +14 -14
- package/.next/server/app/settings/tokens.segments/_full.segment.rsc +14 -14
- package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings/tokens.segments/_index.segment.rsc +7 -7
- package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +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 +2 -2
- package/.next/server/app/settings.rsc +14 -14
- package/.next/server/app/settings.segments/_full.segment.rsc +14 -14
- package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings.segments/_index.segment.rsc +7 -7
- package/.next/server/app/settings.segments/_tree.segment.rsc +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/build-manifest.json +5 -5
- package/.next/server/app/tasks/[id]/page/next-font-manifest.json +1 -1
- package/.next/server/app/tasks/[id]/page.js +13 -13
- 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/build-manifest.json +5 -5
- package/.next/server/app/tasks/page/next-font-manifest.json +1 -1
- package/.next/server/app/tasks/page.js +13 -13
- 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 +2 -2
- package/.next/server/app/tasks.rsc +14 -14
- package/.next/server/app/tasks.segments/_full.segment.rsc +14 -14
- package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
- package/.next/server/app/tasks.segments/_index.segment.rsc +7 -7
- package/.next/server/app/tasks.segments/_tree.segment.rsc +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 +1 -0
- package/.next/server/chunks/00jm_next_04f3gac._.js +10 -0
- package/.next/server/chunks/00jm_next_04f3gac._.js.map +1 -0
- package/.next/server/chunks/00jm_next_dist_esm_build_templates_app-route_02f430o.js +3 -0
- package/.next/server/chunks/00jm_next_dist_esm_build_templates_app-route_02f430o.js.map +1 -0
- package/.next/server/chunks/0ci9_nt-network-dashboard__next-internal_server_app_api_auth_v3_route_actions_0f5nbzr.js +3 -0
- package/.next/server/chunks/0ci9_nt-network-dashboard__next-internal_server_app_favicon_ico_route_actions_0t0eu9r.js +3 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_anet_config_route_actions_0a~v55_.js +3 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_auth_login_route_actions_0xnxfs4.js +3 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_auth_logout_route_actions_0p8p0rn.js +3 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_audit-log_route_actions_06uquwi.js +3 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_auth_route_actions_0f_y1ey.js +3 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_broadcast_route_actions_0ao3j43.js +3 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_events_route_actions_0_5ndld.js +3 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_health_route_actions_0mpzl74.js +3 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_inbox_route_actions_0stoyto.js +3 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_license_route_actions_0a4.fuh.js +3 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_messages_route_actions_059rw1o.js +3 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_networks_route_actions_03sehpn.js +3 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_nodes_route_actions_11fbp28.js +3 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_register_route_actions_0l.d7~n.js +3 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_send_route_actions_0.p82vy.js +3 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_server-logs_route_actions_05~c5lb.js +3 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_servers_route_actions_08-m-cs.js +3 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_session_route_actions_0b9ef2i.js +3 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_stats_route_actions_03u65u0.js +3 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_status_route_actions_0_71ear.js +3 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_task-events_route_actions_07md_ee.js +3 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_tasks_route_actions_0er3uvk.js +3 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_tmux_route_actions_0o_1-8u.js +3 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_tokens_route_actions_09doluz.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0-2le8-._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0-2le8-._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0-g~15e._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0-g~15e._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__00194.t._.js +3 -0
- package/.next/server/chunks/{[root-of-the-server]__03ueci3._.js.map → [root-of-the-server]__00194.t._.js.map} +1 -1
- package/.next/server/chunks/[root-of-the-server]__02p2n3n._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__02p2n3n._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__02y-i-p._.js +6 -0
- package/.next/server/chunks/[root-of-the-server]__02y-i-p._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__043tnx2._.js +3 -0
- package/.next/server/chunks/{[root-of-the-server]__0o4qfn9._.js.map → [root-of-the-server]__043tnx2._.js.map} +1 -1
- package/.next/server/chunks/[root-of-the-server]__04z62zk._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__04z62zk._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__06p6kkp._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__06p6kkp._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__07ztet2._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__07ztet2._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__08uyvdq._.js +20 -0
- package/.next/server/chunks/[root-of-the-server]__08uyvdq._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0b7v51y._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0b7v51y._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0e3ucl1._.js +3 -0
- package/.next/server/chunks/{[root-of-the-server]__0rg9ifx._.js.map → [root-of-the-server]__0e3ucl1._.js.map} +1 -1
- package/.next/server/chunks/[root-of-the-server]__0e6yail._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0e6yail._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0gx2_6a._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0gx2_6a._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0hcnyp.._.js +3 -0
- package/.next/server/chunks/{[root-of-the-server]__0o29v8c._.js.map → [root-of-the-server]__0hcnyp.._.js.map} +1 -1
- package/.next/server/chunks/[root-of-the-server]__0hg3_8r._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0hg3_8r._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0mxw4vb._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0mxw4vb._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0mzopco._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0mzopco._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0o45a4q._.js +3 -0
- package/.next/server/chunks/{[root-of-the-server]__0p6tutt._.js.map → [root-of-the-server]__0o45a4q._.js.map} +1 -1
- package/.next/server/chunks/[root-of-the-server]__0q-teo6._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0q-teo6._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0qcxxzf._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0qcxxzf._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0rovr5-._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0rovr5-._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0tx8s8i._.js +3 -0
- package/.next/server/chunks/{[root-of-the-server]__0~h2uqi._.js.map → [root-of-the-server]__0tx8s8i._.js.map} +1 -1
- package/.next/server/chunks/[root-of-the-server]__0xtp-et._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0xtp-et._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__10pvabe._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__10pvabe._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__10t11zq._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__10t11zq._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__12r~omy._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__12r~omy._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__1389~cs._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__1389~cs._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__13n8t..._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__13n8t..._.js.map +1 -0
- package/.next/server/chunks/[turbopack]_runtime.js +1 -1
- package/.next/server/chunks/ssr/{node_modules_0r_.s40._.js → 00jm_03rn9vn._.js} +2 -2
- package/.next/server/chunks/ssr/00jm_03rn9vn._.js.map +1 -0
- package/.next/server/chunks/ssr/{node_modules_09w7yel._.js → 00jm_059ns68._.js} +3 -3
- package/.next/server/chunks/ssr/00jm_059ns68._.js.map +1 -0
- package/.next/server/chunks/ssr/00jm_09ae-q0._.js +3 -0
- package/.next/server/chunks/ssr/00jm_09ae-q0._.js.map +1 -0
- package/.next/server/chunks/ssr/00jm_0j7__z.._.js +3 -0
- package/.next/server/chunks/ssr/00jm_0j7__z.._.js.map +1 -0
- package/.next/server/chunks/ssr/00jm_next_01-0urn._.js +6 -0
- package/.next/server/chunks/ssr/00jm_next_01-0urn._.js.map +1 -0
- package/.next/server/chunks/ssr/00jm_next_dist_05rlh5d._.js +6 -0
- package/.next/server/chunks/ssr/00jm_next_dist_05rlh5d._.js.map +1 -0
- package/.next/server/chunks/ssr/00jm_next_dist_client_components_0gkudcq._.js +3 -0
- package/.next/server/chunks/ssr/00jm_next_dist_client_components_0gkudcq._.js.map +1 -0
- package/.next/server/chunks/ssr/00jm_next_dist_client_components_builtin_forbidden_0ecus3e.js +3 -0
- package/.next/server/chunks/ssr/00jm_next_dist_client_components_builtin_forbidden_0ecus3e.js.map +1 -0
- package/.next/server/chunks/ssr/00jm_next_dist_client_components_builtin_global-error_07n8pg2.js +3 -0
- package/.next/server/chunks/ssr/00jm_next_dist_client_components_builtin_global-error_07n8pg2.js.map +1 -0
- package/.next/server/chunks/ssr/00jm_next_dist_client_components_builtin_unauthorized_0r0uq4j.js +3 -0
- package/.next/server/chunks/ssr/00jm_next_dist_client_components_builtin_unauthorized_0r0uq4j.js.map +1 -0
- package/.next/server/chunks/ssr/{node_modules_next_dist_esm_build_templates_app-page_002l7yi.js → 00jm_next_dist_esm_build_templates_app-page_0..ddwm.js} +3 -3
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0..ddwm.js.map +1 -0
- package/.next/server/chunks/ssr/{node_modules_next_dist_esm_build_templates_app-page_0qm1-a1.js → 00jm_next_dist_esm_build_templates_app-page_02bxi6j.js} +3 -3
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_02bxi6j.js.map +1 -0
- package/.next/server/chunks/ssr/{node_modules_next_dist_esm_build_templates_app-page_02suzhc.js → 00jm_next_dist_esm_build_templates_app-page_05oassd.js} +3 -3
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_05oassd.js.map +1 -0
- package/.next/server/chunks/ssr/{node_modules_next_dist_esm_build_templates_app-page_0dm~kj8.js → 00jm_next_dist_esm_build_templates_app-page_05vxtby.js} +3 -3
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_05vxtby.js.map +1 -0
- package/.next/server/chunks/ssr/{node_modules_next_dist_esm_build_templates_app-page_0uccf9z.js → 00jm_next_dist_esm_build_templates_app-page_05~eml4.js} +3 -3
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_05~eml4.js.map +1 -0
- package/.next/server/chunks/ssr/{node_modules_next_dist_esm_build_templates_app-page_0l-fh0i.js → 00jm_next_dist_esm_build_templates_app-page_0_-g_q2.js} +3 -3
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0_-g_q2.js.map +1 -0
- package/.next/server/chunks/ssr/{node_modules_next_dist_esm_build_templates_app-page_0uv6jyc.js → 00jm_next_dist_esm_build_templates_app-page_0_ec0xu.js} +3 -3
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0_ec0xu.js.map +1 -0
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0czb6wz.js +4 -0
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0czb6wz.js.map +1 -0
- package/.next/server/chunks/ssr/{node_modules_next_dist_esm_build_templates_app-page_13s07li.js → 00jm_next_dist_esm_build_templates_app-page_0e-cn.p.js} +3 -3
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0e-cn.p.js.map +1 -0
- package/.next/server/chunks/ssr/{node_modules_next_dist_esm_build_templates_app-page_0o9vt1n.js → 00jm_next_dist_esm_build_templates_app-page_0k8_sr8.js} +3 -3
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0k8_sr8.js.map +1 -0
- package/.next/server/chunks/ssr/{node_modules_next_dist_esm_build_templates_app-page_0nsg22r.js → 00jm_next_dist_esm_build_templates_app-page_0lt_3bw.js} +3 -3
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0lt_3bw.js.map +1 -0
- package/.next/server/chunks/ssr/{node_modules_next_dist_esm_build_templates_app-page_0d2yq5t.js → 00jm_next_dist_esm_build_templates_app-page_0uyh3yt.js} +3 -3
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0uyh3yt.js.map +1 -0
- package/.next/server/chunks/ssr/{node_modules_next_dist_esm_build_templates_app-page_0foejvu.js → 00jm_next_dist_esm_build_templates_app-page_0v-a2ps.js} +3 -3
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0v-a2ps.js.map +1 -0
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_12.4r9~.js +4 -0
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_12.4r9~.js.map +1 -0
- package/.next/server/chunks/ssr/{node_modules_next_dist_esm_build_templates_app-page_07uu-xq.js → 00jm_next_dist_esm_build_templates_app-page_13mqkay.js} +3 -3
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_13mqkay.js.map +1 -0
- package/.next/server/chunks/ssr/0dwh_ent-network-dashboard__next-internal_server_app_server-logs_page_actions_0hkk815.js +3 -0
- package/.next/server/chunks/ssr/0yas_gent-network-dashboard__next-internal_server_app__not-found_page_actions_0u.f-rz.js +3 -0
- package/.next/server/chunks/ssr/0yas_gent-network-dashboard__next-internal_server_app_tasks_[id]_page_actions_0r3~lho.js +3 -0
- package/.next/server/chunks/ssr/0ykm__next-internal_server_app__global-error_page_actions_100fxls.js +3 -0
- package/.next/server/chunks/ssr/0ykm__next-internal_server_app_settings_networks_page_actions_0jgy57m.js +3 -0
- package/.next/server/chunks/ssr/0ykm__next-internal_server_app_settings_tokens_page_actions_0l5h.k~.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__030vg4n._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__030vg4n._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__063~dul._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__063~dul._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__07io.d8._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__07io.d8._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__08d6lp4._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__08d6lp4._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__09f1wn6._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__09f1wn6._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0c4xtkg._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0c4xtkg._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0erfzjn._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0erfzjn._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0ezjwje._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0ezjwje._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js.map +1 -0
- package/.next/server/chunks/ssr/{[root-of-the-server]__08qr2ji._.js → [root-of-the-server]__0gc.4uk._.js} +3 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0gc.4uk._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0h_p~-4._.js +19 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0h_p~-4._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0jdr1e5._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0jdr1e5._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0lw16mu._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0lw16mu._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0mjly~-._.js +33 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0mjly~-._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0p-95r_._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0p-95r_._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0u5aqkk._.js +8 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0u5aqkk._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0ue5.fy._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0ue5.fy._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0uggxvx._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0uggxvx._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0zjk5ro._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0zjk5ro._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0zz3v7a._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0zz3v7a._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0~y12ef._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0~y12ef._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__129sc_9._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__129sc_9._.js.map +1 -0
- package/.next/server/chunks/ssr/[turbopack]_runtime.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +3 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -0
- package/.next/server/chunks/ssr/agent-network-dashboard__next-internal_server_app_admin_page_actions_0i~.x~j.js +3 -0
- package/.next/server/chunks/ssr/agent-network-dashboard__next-internal_server_app_login_page_actions_0frwl15.js +3 -0
- package/.next/server/chunks/ssr/agent-network-dashboard__next-internal_server_app_logs_page_actions_0xa2ox_.js +3 -0
- package/.next/server/chunks/ssr/agent-network-dashboard__next-internal_server_app_messages_page_actions_10j3tg4.js +3 -0
- package/.next/server/chunks/ssr/agent-network-dashboard__next-internal_server_app_node_page_actions_0sl4ntd.js +3 -0
- package/.next/server/chunks/ssr/agent-network-dashboard__next-internal_server_app_nodes_page_actions_0cpgkfs.js +3 -0
- package/.next/server/chunks/ssr/agent-network-dashboard__next-internal_server_app_page_actions_08nuo36.js +3 -0
- package/.next/server/chunks/ssr/agent-network-dashboard__next-internal_server_app_settings_page_actions_0iv.lmk.js +3 -0
- package/.next/server/chunks/ssr/agent-network-dashboard__next-internal_server_app_tasks_page_actions_08c7qlx.js +3 -0
- package/.next/server/chunks/ssr/agent-network-dashboard__next-internal_server_app_tasks_page_actions_08c7qlx.js.map +1 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +3 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_057q.ne._.js +3 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_057q.ne._.js.map +1 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +3 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js +3 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js.map +1 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js +3 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js.map +1 -0
- package/.next/server/chunks/ssr/{app_0izi28j._.js → agent-network-dashboard_app_0xgney8._.js} +2 -2
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js.map +1 -0
- package/.next/server/chunks/ssr/{app_0_agk0r._.js → agent-network-dashboard_app_10hjgv4._.js} +2 -2
- package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js.map +1 -0
- package/.next/server/chunks/ssr/{app_0.zbtn5._.js → agent-network-dashboard_app_1153xeb._.js} +2 -2
- package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js.map +1 -0
- package/.next/server/chunks/ssr/{app_04n78pr._.js → agent-network-dashboard_app_12l4oto._.js} +2 -2
- package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js.map +1 -0
- package/.next/server/chunks/ssr/{app_components_08ys5y3._.js → agent-network-dashboard_app_components_0mvyi-4._.js} +3 -3
- package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js.map +1 -0
- package/.next/server/chunks/ssr/{app_server-logs_page_tsx_0_kpp1p._.js → agent-network-dashboard_app_server-logs_page_tsx_0dg.l_8._.js} +2 -2
- package/.next/server/chunks/ssr/agent-network-dashboard_app_server-logs_page_tsx_0dg.l_8._.js.map +1 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_tasks_page_tsx_0mwxy4z._.js +3 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_tasks_page_tsx_0mwxy4z._.js.map +1 -0
- package/.next/server/middleware-build-manifest.js +8 -8
- package/.next/server/middleware.js +3 -3
- package/.next/server/middleware.js.nft.json +1 -1
- package/.next/server/next-font-manifest.js +1 -1
- package/.next/server/next-font-manifest.json +14 -14
- package/.next/server/pages/404.html +2 -2
- 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-7iowzu4.798.js +1 -0
- package/.next/static/chunks/{0gtbwcilfu6b7.js → 0.66f3.rtcybb.js} +1 -1
- package/.next/static/chunks/01u5zo8b7r_r3.js +1 -0
- package/.next/static/chunks/02t5jr4shh.hy.js +1 -0
- package/.next/static/chunks/039-b.v_9-_rm.js +1 -0
- package/.next/static/chunks/05oi25hh7b81r.js +31 -0
- package/.next/static/chunks/07~8sx3_5uiox.js +1 -0
- package/.next/static/chunks/09nd.t_hd.y6t.css +2 -0
- package/.next/static/chunks/{0pqt~8bl3ukh4.js → 0_pxg.4pebw__.js} +2 -2
- package/.next/static/chunks/0_qfg5s5uzs7..js +7 -0
- package/.next/static/chunks/0b8wnt4put-5n.js +2 -0
- package/.next/static/chunks/0elalb9n_.~di.js +1 -0
- package/.next/static/chunks/{01xlw8hd842-c.js → 0eov~nkwaf-qa.js} +1 -1
- package/.next/static/chunks/0fqit.b4j_6rz.js +1 -0
- package/.next/static/chunks/0j_q__o8p6g83.js +1 -0
- package/.next/static/chunks/0jl6_w50zh4l5.js +1 -0
- package/.next/static/chunks/{0w1zwt8kq_7nb.js → 0ku0fjqlm9mca.js} +1 -1
- package/.next/static/chunks/{0-hlcuoemhe7w.js → 0pl7l_.2im.j0.js} +2 -2
- package/.next/static/chunks/0qcfcgdthhos6.js +1 -0
- package/.next/static/chunks/0r4q73.-1bkyl.js +1 -0
- package/.next/static/chunks/0r_c8eccg3z2o.js +1 -0
- package/.next/static/chunks/0tvn2l1pc.h65.js +1 -0
- package/.next/static/chunks/0u3b93reo9xci.js +1 -0
- package/.next/static/chunks/10oi7l-tycgl9.js +1 -0
- package/.next/static/chunks/11uk1izuv3~0t.js +1 -0
- package/.next/static/chunks/{turbopack-01f1xs~cq_~si.js → turbopack-0.7dvsf9rllt0.js} +1 -1
- package/.next/trace +2 -2
- package/.next/trace-build +1 -1
- package/.next/types/routes.d.ts +2 -1
- package/.next/types/validator.ts +9 -0
- package/app/api/hub/servers/route.ts +39 -0
- package/app/components/AppShell.tsx +2 -0
- package/app/components/ChatPopover.tsx +6 -6
- package/app/components/ServersDrawer.tsx +206 -0
- package/app/components/TaskChatPanel.tsx +39 -39
- package/app/components/TopoGraph.tsx +739 -163
- package/app/components/types.ts +7 -0
- package/app/lib/vendorIdentity.ts +145 -0
- package/app/page.tsx +18 -1
- package/package.json +1 -1
- package/scripts/chat-popover-test.mjs +2 -0
- package/scripts/chat-theme-test.mjs +98 -0
- package/scripts/servers-drawer-live-test.mjs +96 -0
- package/scripts/topo-arrow-tier-test.mjs +61 -0
- package/scripts/topo-chat-focus-ring-test.mjs +77 -0
- package/scripts/topo-click-ripple-test.mjs +66 -0
- package/scripts/topo-group-count-test.mjs +63 -0
- package/scripts/topo-group-hover-test.mjs +82 -0
- package/scripts/topo-groupbox-test.mjs +118 -0
- package/scripts/topo-label-scale-test.mjs +78 -0
- package/scripts/topo-layout-test.mjs +1 -0
- package/scripts/topo-offline-dim-test.mjs +70 -0
- package/scripts/topo-overlap-test.mjs +137 -0
- package/scripts/topo-p0round-test.mjs +166 -0
- package/scripts/topo-pan-cursor-test.mjs +62 -0
- package/scripts/topo-rename-test.mjs +107 -0
- package/scripts/topo-vendor-test.mjs +97 -0
- package/scripts/topo-wheel-zoom-test.mjs +79 -0
- package/scripts/topo-zoom-keys-test.mjs +89 -0
- package/.next/build/chunks/[turbopack-node]_transforms_postcss_ts_06e.r3r._.js +0 -13
- package/.next/build/chunks/node_modules_13sb.px._.js.map +0 -47
- package/.next/server/chunks/[root-of-the-server]__0-mykaf._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0-mykaf._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__0.d_kqy._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0.d_kqy._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__02pocyg._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__02pocyg._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__03ueci3._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__04aud7x._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__04aud7x._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__05fnt3n._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__05fnt3n._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__077tsd.._.js +0 -20
- package/.next/server/chunks/[root-of-the-server]__077tsd.._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__08wy1jr._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__08wy1jr._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__0adhu1m._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0adhu1m._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__0aqv02v._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0aqv02v._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__0cawhfq._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0cawhfq._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__0d_-_k.._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0d_-_k.._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__0f6bxfl._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0f6bxfl._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__0h15edq._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0h15edq._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__0k.vegl._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0k.vegl._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__0kltbce._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0kltbce._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__0mn04fw._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0mn04fw._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__0o29v8c._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0o4qfn9._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0o7ya7r._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0o7ya7r._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__0p3fo6l._.js +0 -6
- package/.next/server/chunks/[root-of-the-server]__0p3fo6l._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__0p6tutt._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0pn3wsx._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0pn3wsx._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__0rg9ifx._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0x.-194._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0x.-194._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__0x.a_yh._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0x.a_yh._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__0~h2uqi._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__139~d6a._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__139~d6a._.js.map +0 -1
- package/.next/server/chunks/_next-internal_server_app_api_anet_config_route_actions_0xzy73j.js +0 -3
- package/.next/server/chunks/_next-internal_server_app_api_auth_login_route_actions_0zukc38.js +0 -3
- package/.next/server/chunks/_next-internal_server_app_api_auth_logout_route_actions_0fm~3ij.js +0 -3
- package/.next/server/chunks/_next-internal_server_app_api_auth_v3_route_actions_0ndzs5l.js +0 -3
- package/.next/server/chunks/_next-internal_server_app_api_hub_audit-log_route_actions_11k8fqt.js +0 -3
- package/.next/server/chunks/_next-internal_server_app_api_hub_auth_route_actions_0gs7ttk.js +0 -3
- package/.next/server/chunks/_next-internal_server_app_api_hub_broadcast_route_actions_0ads-kd.js +0 -3
- package/.next/server/chunks/_next-internal_server_app_api_hub_events_route_actions_0opktby.js +0 -3
- package/.next/server/chunks/_next-internal_server_app_api_hub_health_route_actions_0tzzt~x.js +0 -3
- package/.next/server/chunks/_next-internal_server_app_api_hub_inbox_route_actions_0xvslrg.js +0 -3
- package/.next/server/chunks/_next-internal_server_app_api_hub_license_route_actions_0i3tlr3.js +0 -3
- package/.next/server/chunks/_next-internal_server_app_api_hub_messages_route_actions_08-gi-s.js +0 -3
- package/.next/server/chunks/_next-internal_server_app_api_hub_networks_route_actions_02vejq2.js +0 -3
- package/.next/server/chunks/_next-internal_server_app_api_hub_nodes_route_actions_0nx8ryn.js +0 -3
- package/.next/server/chunks/_next-internal_server_app_api_hub_register_route_actions_086wi_3.js +0 -3
- package/.next/server/chunks/_next-internal_server_app_api_hub_send_route_actions_0-ga_ug.js +0 -3
- package/.next/server/chunks/_next-internal_server_app_api_hub_server-logs_route_actions_00euexv.js +0 -3
- package/.next/server/chunks/_next-internal_server_app_api_hub_session_route_actions_041wa62.js +0 -3
- package/.next/server/chunks/_next-internal_server_app_api_hub_stats_route_actions_1242~od.js +0 -3
- package/.next/server/chunks/_next-internal_server_app_api_hub_status_route_actions_0aml09a.js +0 -3
- package/.next/server/chunks/_next-internal_server_app_api_hub_task-events_route_actions_02v1tmm.js +0 -3
- package/.next/server/chunks/_next-internal_server_app_api_hub_tasks_route_actions_0nl77v4.js +0 -3
- package/.next/server/chunks/_next-internal_server_app_api_hub_tmux_route_actions_0u~aaza.js +0 -3
- package/.next/server/chunks/_next-internal_server_app_api_hub_tokens_route_actions_09hzf_1.js +0 -3
- package/.next/server/chunks/_next-internal_server_app_favicon_ico_route_actions_095lj93.js +0 -3
- package/.next/server/chunks/node_modules_next_11synfn._.js +0 -10
- package/.next/server/chunks/node_modules_next_11synfn._.js.map +0 -1
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_0yubv2d.js +0 -3
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_0yubv2d.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__014uja_._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__014uja_._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__02hi65f._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__02hi65f._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__02xqa5z._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__02xqa5z._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__04ph1de._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__04ph1de._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__05wc5-z._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__05wc5-z._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__06-2p1a._.js +0 -33
- package/.next/server/chunks/ssr/[root-of-the-server]__06-2p1a._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__08qr2ji._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__09l8xaa._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__09l8xaa._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__09z7o2x._.js +0 -19
- package/.next/server/chunks/ssr/[root-of-the-server]__09z7o2x._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0a0ffmv._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0a0ffmv._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0b7uu-_._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0b7uu-_._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0bl_wqk._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0bl_wqk._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0ft0rpu._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0ft0rpu._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0i~g-2t._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0i~g-2t._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0jl4ukn._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0jl4ukn._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0mook08._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0mook08._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0n46s~k._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0n46s~k._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0qchqb~._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0qchqb~._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0rrm~-q._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0rrm~-q._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0w2ay2h._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0w2ay2h._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0xjish~._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0xjish~._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0xm3h9x._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0xm3h9x._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0ywihy_._.js +0 -8
- package/.next/server/chunks/ssr/[root-of-the-server]__0ywihy_._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__12ad1n.._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__12ad1n.._.js.map +0 -1
- package/.next/server/chunks/ssr/_0nne7mz._.js +0 -3
- package/.next/server/chunks/ssr/_0nne7mz._.js.map +0 -1
- package/.next/server/chunks/ssr/_next-internal_server_app__global-error_page_actions_0k77kol.js +0 -3
- package/.next/server/chunks/ssr/_next-internal_server_app__not-found_page_actions_0eq97pa.js +0 -3
- package/.next/server/chunks/ssr/_next-internal_server_app_admin_page_actions_0ycnu8f.js +0 -3
- package/.next/server/chunks/ssr/_next-internal_server_app_login_page_actions_02kefem.js +0 -3
- package/.next/server/chunks/ssr/_next-internal_server_app_logs_page_actions_0rvsl~d.js +0 -3
- package/.next/server/chunks/ssr/_next-internal_server_app_messages_page_actions_0gsqep_.js +0 -3
- package/.next/server/chunks/ssr/_next-internal_server_app_node_page_actions_0wd8cgl.js +0 -3
- package/.next/server/chunks/ssr/_next-internal_server_app_nodes_page_actions_0-.rv67.js +0 -3
- package/.next/server/chunks/ssr/_next-internal_server_app_page_actions_09-gtaw.js +0 -3
- package/.next/server/chunks/ssr/_next-internal_server_app_server-logs_page_actions_0g1gb5c.js +0 -3
- package/.next/server/chunks/ssr/_next-internal_server_app_settings_networks_page_actions_09uw.sw.js +0 -3
- package/.next/server/chunks/ssr/_next-internal_server_app_settings_page_actions_0mr68ai.js +0 -3
- package/.next/server/chunks/ssr/_next-internal_server_app_settings_tokens_page_actions_0yi54x8.js +0 -3
- package/.next/server/chunks/ssr/_next-internal_server_app_tasks_[id]_page_actions_0k_hm9g.js +0 -3
- package/.next/server/chunks/ssr/_next-internal_server_app_tasks_page_actions_0_2o_cr.js +0 -3
- package/.next/server/chunks/ssr/app_0.zbtn5._.js.map +0 -1
- package/.next/server/chunks/ssr/app_01y-~mp._.js +0 -3
- package/.next/server/chunks/ssr/app_01y-~mp._.js.map +0 -1
- package/.next/server/chunks/ssr/app_04n78pr._.js.map +0 -1
- package/.next/server/chunks/ssr/app_0_agk0r._.js.map +0 -1
- package/.next/server/chunks/ssr/app_0aenxef._.js +0 -3
- package/.next/server/chunks/ssr/app_0aenxef._.js.map +0 -1
- package/.next/server/chunks/ssr/app_0aum_g5._.js +0 -3
- package/.next/server/chunks/ssr/app_0aum_g5._.js.map +0 -1
- package/.next/server/chunks/ssr/app_0emvkn2._.js +0 -3
- package/.next/server/chunks/ssr/app_0emvkn2._.js.map +0 -1
- package/.next/server/chunks/ssr/app_0esuljp._.js +0 -3
- package/.next/server/chunks/ssr/app_0esuljp._.js.map +0 -1
- package/.next/server/chunks/ssr/app_0izi28j._.js.map +0 -1
- package/.next/server/chunks/ssr/app_components_08ys5y3._.js.map +0 -1
- package/.next/server/chunks/ssr/app_server-logs_page_tsx_0_kpp1p._.js.map +0 -1
- package/.next/server/chunks/ssr/app_tasks_page_tsx_0z-d6mp._.js +0 -3
- package/.next/server/chunks/ssr/app_tasks_page_tsx_0z-d6mp._.js.map +0 -1
- package/.next/server/chunks/ssr/node_modules_09w7yel._.js.map +0 -1
- package/.next/server/chunks/ssr/node_modules_0ck2~9g._.js +0 -3
- package/.next/server/chunks/ssr/node_modules_0ck2~9g._.js.map +0 -1
- package/.next/server/chunks/ssr/node_modules_0r_.s40._.js.map +0 -1
- package/.next/server/chunks/ssr/node_modules_0vtdjw7._.js +0 -3
- package/.next/server/chunks/ssr/node_modules_0vtdjw7._.js.map +0 -1
- package/.next/server/chunks/ssr/node_modules_next_0w2y7fz._.js +0 -6
- package/.next/server/chunks/ssr/node_modules_next_0w2y7fz._.js.map +0 -1
- package/.next/server/chunks/ssr/node_modules_next_dist_0e1izl_._.js +0 -6
- package/.next/server/chunks/ssr/node_modules_next_dist_0e1izl_._.js.map +0 -1
- package/.next/server/chunks/ssr/node_modules_next_dist_client_components_0inhx6q._.js +0 -3
- package/.next/server/chunks/ssr/node_modules_next_dist_client_components_0inhx6q._.js.map +0 -1
- package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_forbidden_0ghu-f7.js +0 -3
- package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_forbidden_0ghu-f7.js.map +0 -1
- package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_global-error_0lgvd_..js +0 -3
- package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_global-error_0lgvd_..js.map +0 -1
- package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_unauthorized_0cjv-23.js +0 -3
- package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_unauthorized_0cjv-23.js.map +0 -1
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_002l7yi.js.map +0 -1
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_02suzhc.js.map +0 -1
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_07uu-xq.js.map +0 -1
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0d2yq5t.js.map +0 -1
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0dm~kj8.js.map +0 -1
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0foejvu.js.map +0 -1
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0l-fh0i.js.map +0 -1
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0nsg22r.js.map +0 -1
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0o9vt1n.js.map +0 -1
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0qm1-a1.js.map +0 -1
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0uccf9z.js.map +0 -1
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0uv6jyc.js.map +0 -1
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_10~~o-h.js +0 -4
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_10~~o-h.js.map +0 -1
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_13leyhk.js +0 -4
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_13leyhk.js.map +0 -1
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_13s07li.js.map +0 -1
- package/.next/static/chunks/0218jmcfde~9s.js +0 -1
- package/.next/static/chunks/0257pdz1-imal.js +0 -2
- package/.next/static/chunks/02_zpo7taex9o.js +0 -7
- package/.next/static/chunks/06n3d~fbbtagk.js +0 -1
- package/.next/static/chunks/07eba1lz2u3p4.js +0 -1
- package/.next/static/chunks/09n9xs.6dvvo2.css +0 -2
- package/.next/static/chunks/0av2~0.96~d~g.js +0 -1
- package/.next/static/chunks/0c1bp~g7.8ru..js +0 -1
- package/.next/static/chunks/0e-.zgw81gela.js +0 -31
- package/.next/static/chunks/0g9aof0ob_fcj.js +0 -1
- package/.next/static/chunks/0s8jo6jypzkx5.js +0 -1
- package/.next/static/chunks/0u359llqm1_01.js +0 -1
- package/.next/static/chunks/0ubqqhhpsaez4.js +0 -1
- package/.next/static/chunks/0z1xslnw3jx1s.js +0 -1
- package/.next/static/chunks/0~qw9kxksr1v2.js +0 -1
- package/.next/static/chunks/12gprw3l0kvvf.js +0 -1
- package/.next/static/chunks/12yuo353giomx.js +0 -1
- package/.next/static/chunks/13j9vr8_l9ket.js +0 -1
- package/.next/static/chunks/14bsg3_-taiw4.js +0 -1
- /package/.next/build/chunks/{[root-of-the-server]__0ubbtyl._.js.map → [root-of-the-server]__0tgc-8p._.js.map} +0 -0
- /package/.next/build/chunks/{[turbopack-node]_transforms_postcss_ts_06e.r3r._.js.map → [turbopack-node]_transforms_postcss_ts_0r4x5ys._.js.map} +0 -0
- /package/.next/server/chunks/{_next-internal_server_app_api_anet_config_route_actions_0xzy73j.js.map → 0ci9_nt-network-dashboard__next-internal_server_app_api_auth_v3_route_actions_0f5nbzr.js.map} +0 -0
- /package/.next/server/chunks/{_next-internal_server_app_api_auth_login_route_actions_0zukc38.js.map → 0ci9_nt-network-dashboard__next-internal_server_app_favicon_ico_route_actions_0t0eu9r.js.map} +0 -0
- /package/.next/server/chunks/{_next-internal_server_app_api_auth_logout_route_actions_0fm~3ij.js.map → 0ykm__next-internal_server_app_api_anet_config_route_actions_0a~v55_.js.map} +0 -0
- /package/.next/server/chunks/{_next-internal_server_app_api_auth_v3_route_actions_0ndzs5l.js.map → 0ykm__next-internal_server_app_api_auth_login_route_actions_0xnxfs4.js.map} +0 -0
- /package/.next/server/chunks/{_next-internal_server_app_api_hub_audit-log_route_actions_11k8fqt.js.map → 0ykm__next-internal_server_app_api_auth_logout_route_actions_0p8p0rn.js.map} +0 -0
- /package/.next/server/chunks/{_next-internal_server_app_api_hub_auth_route_actions_0gs7ttk.js.map → 0ykm__next-internal_server_app_api_hub_audit-log_route_actions_06uquwi.js.map} +0 -0
- /package/.next/server/chunks/{_next-internal_server_app_api_hub_broadcast_route_actions_0ads-kd.js.map → 0ykm__next-internal_server_app_api_hub_auth_route_actions_0f_y1ey.js.map} +0 -0
- /package/.next/server/chunks/{_next-internal_server_app_api_hub_events_route_actions_0opktby.js.map → 0ykm__next-internal_server_app_api_hub_broadcast_route_actions_0ao3j43.js.map} +0 -0
- /package/.next/server/chunks/{_next-internal_server_app_api_hub_health_route_actions_0tzzt~x.js.map → 0ykm__next-internal_server_app_api_hub_events_route_actions_0_5ndld.js.map} +0 -0
- /package/.next/server/chunks/{_next-internal_server_app_api_hub_inbox_route_actions_0xvslrg.js.map → 0ykm__next-internal_server_app_api_hub_health_route_actions_0mpzl74.js.map} +0 -0
- /package/.next/server/chunks/{_next-internal_server_app_api_hub_license_route_actions_0i3tlr3.js.map → 0ykm__next-internal_server_app_api_hub_inbox_route_actions_0stoyto.js.map} +0 -0
- /package/.next/server/chunks/{_next-internal_server_app_api_hub_messages_route_actions_08-gi-s.js.map → 0ykm__next-internal_server_app_api_hub_license_route_actions_0a4.fuh.js.map} +0 -0
- /package/.next/server/chunks/{_next-internal_server_app_api_hub_networks_route_actions_02vejq2.js.map → 0ykm__next-internal_server_app_api_hub_messages_route_actions_059rw1o.js.map} +0 -0
- /package/.next/server/chunks/{_next-internal_server_app_api_hub_nodes_route_actions_0nx8ryn.js.map → 0ykm__next-internal_server_app_api_hub_networks_route_actions_03sehpn.js.map} +0 -0
- /package/.next/server/chunks/{_next-internal_server_app_api_hub_register_route_actions_086wi_3.js.map → 0ykm__next-internal_server_app_api_hub_nodes_route_actions_11fbp28.js.map} +0 -0
- /package/.next/server/chunks/{_next-internal_server_app_api_hub_send_route_actions_0-ga_ug.js.map → 0ykm__next-internal_server_app_api_hub_register_route_actions_0l.d7~n.js.map} +0 -0
- /package/.next/server/chunks/{_next-internal_server_app_api_hub_server-logs_route_actions_00euexv.js.map → 0ykm__next-internal_server_app_api_hub_send_route_actions_0.p82vy.js.map} +0 -0
- /package/.next/server/chunks/{_next-internal_server_app_api_hub_session_route_actions_041wa62.js.map → 0ykm__next-internal_server_app_api_hub_server-logs_route_actions_05~c5lb.js.map} +0 -0
- /package/.next/server/chunks/{_next-internal_server_app_api_hub_stats_route_actions_1242~od.js.map → 0ykm__next-internal_server_app_api_hub_servers_route_actions_08-m-cs.js.map} +0 -0
- /package/.next/server/chunks/{_next-internal_server_app_api_hub_status_route_actions_0aml09a.js.map → 0ykm__next-internal_server_app_api_hub_session_route_actions_0b9ef2i.js.map} +0 -0
- /package/.next/server/chunks/{_next-internal_server_app_api_hub_task-events_route_actions_02v1tmm.js.map → 0ykm__next-internal_server_app_api_hub_stats_route_actions_03u65u0.js.map} +0 -0
- /package/.next/server/chunks/{_next-internal_server_app_api_hub_tasks_route_actions_0nl77v4.js.map → 0ykm__next-internal_server_app_api_hub_status_route_actions_0_71ear.js.map} +0 -0
- /package/.next/server/chunks/{_next-internal_server_app_api_hub_tmux_route_actions_0u~aaza.js.map → 0ykm__next-internal_server_app_api_hub_task-events_route_actions_07md_ee.js.map} +0 -0
- /package/.next/server/chunks/{_next-internal_server_app_api_hub_tokens_route_actions_09hzf_1.js.map → 0ykm__next-internal_server_app_api_hub_tasks_route_actions_0er3uvk.js.map} +0 -0
- /package/.next/server/chunks/{_next-internal_server_app_favicon_ico_route_actions_095lj93.js.map → 0ykm__next-internal_server_app_api_hub_tmux_route_actions_0o_1-8u.js.map} +0 -0
- /package/.next/server/chunks/{ssr/_next-internal_server_app__global-error_page_actions_0k77kol.js.map → 0ykm__next-internal_server_app_api_hub_tokens_route_actions_09doluz.js.map} +0 -0
- /package/.next/server/chunks/ssr/{_next-internal_server_app__not-found_page_actions_0eq97pa.js.map → 0dwh_ent-network-dashboard__next-internal_server_app_server-logs_page_actions_0hkk815.js.map} +0 -0
- /package/.next/server/chunks/ssr/{_next-internal_server_app_admin_page_actions_0ycnu8f.js.map → 0yas_gent-network-dashboard__next-internal_server_app__not-found_page_actions_0u.f-rz.js.map} +0 -0
- /package/.next/server/chunks/ssr/{_next-internal_server_app_login_page_actions_02kefem.js.map → 0yas_gent-network-dashboard__next-internal_server_app_tasks_[id]_page_actions_0r3~lho.js.map} +0 -0
- /package/.next/server/chunks/ssr/{_next-internal_server_app_logs_page_actions_0rvsl~d.js.map → 0ykm__next-internal_server_app__global-error_page_actions_100fxls.js.map} +0 -0
- /package/.next/server/chunks/ssr/{_next-internal_server_app_messages_page_actions_0gsqep_.js.map → 0ykm__next-internal_server_app_settings_networks_page_actions_0jgy57m.js.map} +0 -0
- /package/.next/server/chunks/ssr/{_next-internal_server_app_node_page_actions_0wd8cgl.js.map → 0ykm__next-internal_server_app_settings_tokens_page_actions_0l5h.k~.js.map} +0 -0
- /package/.next/server/chunks/ssr/{_next-internal_server_app_nodes_page_actions_0-.rv67.js.map → agent-network-dashboard__next-internal_server_app_admin_page_actions_0i~.x~j.js.map} +0 -0
- /package/.next/server/chunks/ssr/{_next-internal_server_app_page_actions_09-gtaw.js.map → agent-network-dashboard__next-internal_server_app_login_page_actions_0frwl15.js.map} +0 -0
- /package/.next/server/chunks/ssr/{_next-internal_server_app_server-logs_page_actions_0g1gb5c.js.map → agent-network-dashboard__next-internal_server_app_logs_page_actions_0xa2ox_.js.map} +0 -0
- /package/.next/server/chunks/ssr/{_next-internal_server_app_settings_networks_page_actions_09uw.sw.js.map → agent-network-dashboard__next-internal_server_app_messages_page_actions_10j3tg4.js.map} +0 -0
- /package/.next/server/chunks/ssr/{_next-internal_server_app_settings_page_actions_0mr68ai.js.map → agent-network-dashboard__next-internal_server_app_node_page_actions_0sl4ntd.js.map} +0 -0
- /package/.next/server/chunks/ssr/{_next-internal_server_app_settings_tokens_page_actions_0yi54x8.js.map → agent-network-dashboard__next-internal_server_app_nodes_page_actions_0cpgkfs.js.map} +0 -0
- /package/.next/server/chunks/ssr/{_next-internal_server_app_tasks_[id]_page_actions_0k_hm9g.js.map → agent-network-dashboard__next-internal_server_app_page_actions_08nuo36.js.map} +0 -0
- /package/.next/server/chunks/ssr/{_next-internal_server_app_tasks_page_actions_0_2o_cr.js.map → agent-network-dashboard__next-internal_server_app_settings_page_actions_0iv.lmk.js.map} +0 -0
- /package/.next/static/{kXeBUTKSdNooA8p2BTTfp → mgs4E0Fu2JlZsA5DB0XUu}/_buildManifest.js +0 -0
- /package/.next/static/{kXeBUTKSdNooA8p2BTTfp → mgs4E0Fu2JlZsA5DB0XUu}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{kXeBUTKSdNooA8p2BTTfp → mgs4E0Fu2JlZsA5DB0XUu}/_ssgManifest.js +0 -0
|
@@ -4,6 +4,7 @@ import { useEffect, useMemo, useRef, useState } from 'react';
|
|
|
4
4
|
import { Session } from './types';
|
|
5
5
|
import { aliasAvatarColors, aliasInitial } from './AliasAvatar';
|
|
6
6
|
import { ChatPopover } from './ChatPopover';
|
|
7
|
+
import { vendorForModel, runtimeIdentity, identityLine } from '../lib/vendorIdentity';
|
|
7
8
|
|
|
8
9
|
interface MessageFlow {
|
|
9
10
|
from_alias: string;
|
|
@@ -15,6 +16,9 @@ interface MessageFlow {
|
|
|
15
16
|
interface TopoGraphProps {
|
|
16
17
|
sessions: Session[];
|
|
17
18
|
sseSessions: Record<string, number>;
|
|
19
|
+
// #84: a node.renamed event from the Overview's SSE listener. When the
|
|
20
|
+
// currently open chat popover targets `from`, it follows the rename to `to`.
|
|
21
|
+
renameSignal?: { from: string; to: string; ts: number } | null;
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
interface Point {
|
|
@@ -28,6 +32,9 @@ interface FlowLink {
|
|
|
28
32
|
to: string;
|
|
29
33
|
count: number;
|
|
30
34
|
content: string;
|
|
35
|
+
/** ISO timestamp of the most recent message on this edge — drives
|
|
36
|
+
* the Round 10 freshness fade so dormant links recede visually. */
|
|
37
|
+
last_at: string;
|
|
31
38
|
}
|
|
32
39
|
|
|
33
40
|
const cx = 500;
|
|
@@ -81,36 +88,52 @@ function truncate(value: string, max: number) {
|
|
|
81
88
|
return value.length > max ? `${value.slice(0, max - 1)}...` : value;
|
|
82
89
|
}
|
|
83
90
|
|
|
91
|
+
/** Round 12 / Loop: status trio audit.
|
|
92
|
+
*
|
|
93
|
+
* Each (status × theme) cell returns a {primary, halo, text} trio. The trio
|
|
94
|
+
* invariant — keep it when adding states or tweaking shades:
|
|
95
|
+
*
|
|
96
|
+
* light: primary = <hue>-600 halo = <hue>-100 * text = <hue>-800/900
|
|
97
|
+
* dark: primary = <hue>-400/500 halo = <hue>-900 text = <hue>-100
|
|
98
|
+
*
|
|
99
|
+
* * offline.halo light deviates intentionally to slate-200 (#e2e8f0):
|
|
100
|
+
* slate-100 (#f1f5f9) is too close to the panel bg (#f8fafc) and the
|
|
101
|
+
* halo would vanish. The other three rows keep the 100-shade.
|
|
102
|
+
*
|
|
103
|
+
* Audit caught one cross-family drift before this round: online-other halo
|
|
104
|
+
* light was #dbeafe (blue-100) while its primary (#0284c7 sky-600) and text
|
|
105
|
+
* (#0c4a6e sky-900) were sky-family — now sky-100 (#e0f2fe). Tiny visual
|
|
106
|
+
* difference; large hygiene win — every trio is now mono-hue. */
|
|
84
107
|
function nodeStatus(session: Session, isOnline: boolean, isLight: boolean) {
|
|
85
108
|
if (!isOnline) {
|
|
86
109
|
return {
|
|
87
110
|
label: 'offline',
|
|
88
|
-
primary: isLight ? '#94a3b8' : '#6b7280',
|
|
89
|
-
halo: isLight ? '#e2e8f0' : '#111827',
|
|
90
|
-
text: isLight ? '#475569' : '#9ca3af',
|
|
111
|
+
primary: isLight ? '#94a3b8' : '#6b7280', // slate-400 / gray-500
|
|
112
|
+
halo: isLight ? '#e2e8f0' : '#111827', // slate-200* / gray-900
|
|
113
|
+
text: isLight ? '#475569' : '#9ca3af', // slate-600 / gray-400
|
|
91
114
|
};
|
|
92
115
|
}
|
|
93
116
|
if (session.status === 'working') {
|
|
94
117
|
return {
|
|
95
118
|
label: 'working',
|
|
96
|
-
primary: isLight ? '#059669' : '#22c55e',
|
|
97
|
-
halo: isLight ? '#d1fae5' : '#14532d',
|
|
98
|
-
text: isLight ? '#065f46' : '#dcfce7',
|
|
119
|
+
primary: isLight ? '#059669' : '#22c55e', // emerald-600 / green-500
|
|
120
|
+
halo: isLight ? '#d1fae5' : '#14532d', // emerald-100 / green-900
|
|
121
|
+
text: isLight ? '#065f46' : '#dcfce7', // emerald-800 / green-100
|
|
99
122
|
};
|
|
100
123
|
}
|
|
101
124
|
if (session.status === 'idle') {
|
|
102
125
|
return {
|
|
103
126
|
label: 'idle',
|
|
104
|
-
primary: isLight ? '#0d9488' : '#2dd4bf',
|
|
105
|
-
halo: isLight ? '#ccfbf1' : '#134e4a',
|
|
106
|
-
text: isLight ? '#115e59' : '#ccfbf1',
|
|
127
|
+
primary: isLight ? '#0d9488' : '#2dd4bf', // teal-600 / teal-400
|
|
128
|
+
halo: isLight ? '#ccfbf1' : '#134e4a', // teal-100 / teal-900
|
|
129
|
+
text: isLight ? '#115e59' : '#ccfbf1', // teal-800 / teal-100
|
|
107
130
|
};
|
|
108
131
|
}
|
|
109
132
|
return {
|
|
110
133
|
label: session.status || 'online',
|
|
111
|
-
primary: isLight ? '#0284c7' : '#38bdf8',
|
|
112
|
-
halo: isLight ? '#
|
|
113
|
-
text: isLight ? '#0c4a6e' : '#e0f2fe',
|
|
134
|
+
primary: isLight ? '#0284c7' : '#38bdf8', // sky-600 / sky-400
|
|
135
|
+
halo: isLight ? '#e0f2fe' : '#0c4a6e', // sky-100 / sky-900 (was blue-100 — drift fixed Round 12)
|
|
136
|
+
text: isLight ? '#0c4a6e' : '#e0f2fe', // sky-900 / sky-100
|
|
114
137
|
};
|
|
115
138
|
}
|
|
116
139
|
|
|
@@ -236,22 +259,59 @@ function commonPrefix(a: string, b: string): string {
|
|
|
236
259
|
return a.slice(0, i);
|
|
237
260
|
}
|
|
238
261
|
|
|
239
|
-
|
|
240
|
-
|
|
262
|
+
/** #83 + #111: group nodes that share a ≥2-char alias prefix OR a project_dir
|
|
263
|
+
* ("either criterion → same group", Vincent 4724). Union-find over the
|
|
264
|
+
* sessions; the component label prefers the shared project_dir's basename,
|
|
265
|
+
* else the common alias prefix. Returns alias → groupKey. */
|
|
266
|
+
function computeGroups(sessions: { alias: string; project_dir?: string | null }[]): Record<string, string> {
|
|
267
|
+
const n = sessions.length;
|
|
268
|
+
const parent = sessions.map((_, i) => i);
|
|
269
|
+
const find = (i: number): number => {
|
|
270
|
+
while (parent[i] !== i) { parent[i] = parent[parent[i]]; i = parent[i]; }
|
|
271
|
+
return i;
|
|
272
|
+
};
|
|
273
|
+
const union = (a: number, b: number) => {
|
|
274
|
+
const ra = find(a), rb = find(b);
|
|
275
|
+
if (ra !== rb) parent[ra] = rb;
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
// union by shared project_dir
|
|
279
|
+
const byDir: Record<string, number[]> = {};
|
|
280
|
+
sessions.forEach((s, i) => {
|
|
281
|
+
const d = s.project_dir?.trim();
|
|
282
|
+
if (d) (byDir[d] ||= []).push(i);
|
|
283
|
+
});
|
|
284
|
+
for (const idxs of Object.values(byDir)) {
|
|
285
|
+
for (let k = 1; k < idxs.length; k++) union(idxs[0], idxs[k]);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// union by shared ≥2-char alias prefix — sort, link adjacent pairs
|
|
289
|
+
const order = sessions.map((_, i) => i).sort((a, b) => sessions[a].alias.localeCompare(sessions[b].alias));
|
|
290
|
+
for (let k = 0; k + 1 < order.length; k++) {
|
|
291
|
+
if (commonPrefix(sessions[order[k]].alias, sessions[order[k + 1]].alias).length >= 2) {
|
|
292
|
+
union(order[k], order[k + 1]);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// label each connected component
|
|
297
|
+
const comps: Record<number, number[]> = {};
|
|
298
|
+
for (let i = 0; i < n; i++) (comps[find(i)] ||= []).push(i);
|
|
241
299
|
const keys: Record<string, string> = {};
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
const
|
|
248
|
-
if (
|
|
249
|
-
|
|
250
|
-
|
|
300
|
+
for (const members of Object.values(comps)) {
|
|
301
|
+
let label: string;
|
|
302
|
+
if (members.length === 1) {
|
|
303
|
+
label = sessions[members[0]].alias;
|
|
304
|
+
} else {
|
|
305
|
+
const dirs = new Set(members.map(i => sessions[i].project_dir?.trim()).filter(Boolean) as string[]);
|
|
306
|
+
if (dirs.size === 1) {
|
|
307
|
+
const d = [...dirs][0];
|
|
308
|
+
label = d.split('/').filter(Boolean).pop() || d;
|
|
309
|
+
} else {
|
|
310
|
+
label = members.map(i => sessions[i].alias).reduce((a, b) => commonPrefix(a, b));
|
|
311
|
+
if (label.length < 2) label = sessions[members[0]].alias;
|
|
312
|
+
}
|
|
251
313
|
}
|
|
252
|
-
const
|
|
253
|
-
for (let k = i; k <= j; k++) keys[sorted[k]] = key;
|
|
254
|
-
i = j + 1;
|
|
314
|
+
for (const i of members) keys[sessions[i].alias] = label;
|
|
255
315
|
}
|
|
256
316
|
return keys;
|
|
257
317
|
}
|
|
@@ -271,19 +331,27 @@ function buildFlowLinks(messages: MessageFlow[], positions: Record<string, Point
|
|
|
271
331
|
const key = `${message.from_alias}->${message.to_alias}`;
|
|
272
332
|
const current = links.get(key);
|
|
273
333
|
|
|
334
|
+
// Keep the most-recent timestamp per pair so the render can fade
|
|
335
|
+
// dormant edges (Round 10 freshness fade).
|
|
336
|
+
const incoming = message.created_at || '';
|
|
337
|
+
const last_at = !current
|
|
338
|
+
? incoming
|
|
339
|
+
: (incoming > current.last_at ? incoming : current.last_at);
|
|
340
|
+
|
|
274
341
|
links.set(key, {
|
|
275
342
|
key,
|
|
276
343
|
from: message.from_alias,
|
|
277
344
|
to: message.to_alias,
|
|
278
345
|
count: (current?.count || 0) + 1,
|
|
279
346
|
content: current?.content || message.content,
|
|
347
|
+
last_at,
|
|
280
348
|
});
|
|
281
349
|
});
|
|
282
350
|
|
|
283
351
|
return [...links.values()].slice(0, 18);
|
|
284
352
|
}
|
|
285
353
|
|
|
286
|
-
export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
354
|
+
export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProps) {
|
|
287
355
|
const theme = useTheme();
|
|
288
356
|
const isLight = theme === 'light';
|
|
289
357
|
const pal = isLight ? LIGHT_PALETTE : DARK_PALETTE;
|
|
@@ -306,6 +374,20 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
306
374
|
try { localStorage.setItem('anet-topo-layout', next); } catch {}
|
|
307
375
|
return next;
|
|
308
376
|
});
|
|
377
|
+
// Issue #113: node size scale (Vincent 4727 — nodes too big / crowded at
|
|
378
|
+
// ~22 nodes). S/M/L → 0.7/0.84/1.0; default M (one notch down from the old
|
|
379
|
+
// fixed size). Persisted like the layout toggle.
|
|
380
|
+
const [nodeScale, setNodeScale] = useState(0.84);
|
|
381
|
+
useEffect(() => {
|
|
382
|
+
try {
|
|
383
|
+
const saved = parseFloat(localStorage.getItem('anet-topo-nodescale') || '');
|
|
384
|
+
if (saved === 0.7 || saved === 0.84 || saved === 1) setNodeScale(saved);
|
|
385
|
+
} catch {}
|
|
386
|
+
}, []);
|
|
387
|
+
const pickNodeScale = (v: number) => {
|
|
388
|
+
setNodeScale(v);
|
|
389
|
+
try { localStorage.setItem('anet-topo-nodescale', String(v)); } catch {}
|
|
390
|
+
};
|
|
309
391
|
|
|
310
392
|
useEffect(() => {
|
|
311
393
|
const fetchMessages = async () => {
|
|
@@ -332,6 +414,7 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
332
414
|
flowLinks,
|
|
333
415
|
activeAliases,
|
|
334
416
|
groupKeys,
|
|
417
|
+
groupBoxes,
|
|
335
418
|
} = useMemo(() => {
|
|
336
419
|
const sseCount = (s: { alias: string; network_id?: string }) =>
|
|
337
420
|
(s.network_id ? sseSessions[`${s.network_id}:${s.alias}`] : undefined) ?? sseSessions[s.alias];
|
|
@@ -341,36 +424,123 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
341
424
|
// index, so contiguous-in-array becomes contiguous-in-ring, i.e.
|
|
342
425
|
// each team visually clusters. localeCompare keeps CJK ordering sane.
|
|
343
426
|
const byAlias = (a: Session, b: Session) => a.alias.localeCompare(b.alias);
|
|
427
|
+
// #112 umbrella: ghost age-out — an offline node not seen for over a day
|
|
428
|
+
// is almost certainly a deleted node the server `/api/status` still
|
|
429
|
+
// returns (#74 root cause is server-side; this is the dashboard-side
|
|
430
|
+
// fallback so stale ghosts stop cluttering the topology). A missing
|
|
431
|
+
// last_seen_at is kept (conservative — could be a legitimately new node).
|
|
432
|
+
const GHOST_MS = 24 * 60 * 60 * 1000;
|
|
433
|
+
const now = Date.now();
|
|
434
|
+
const isGhost = (s: Session) => {
|
|
435
|
+
if (s.status !== 'offline' || sseCount(s) || !s.last_seen_at) return false;
|
|
436
|
+
const t = Date.parse(s.last_seen_at);
|
|
437
|
+
return !Number.isNaN(t) && now - t > GHOST_MS;
|
|
438
|
+
};
|
|
344
439
|
const online = sessions.filter(s => s.status !== 'offline' || sseCount(s)).sort(byAlias);
|
|
345
|
-
const offline = sessions.filter(s => s.status === 'offline' && !sseCount(s)).sort(byAlias);
|
|
440
|
+
const offline = sessions.filter(s => s.status === 'offline' && !sseCount(s) && !isGhost(s)).sort(byAlias);
|
|
346
441
|
const positions: Record<string, Point> = {};
|
|
347
442
|
|
|
348
443
|
if (layout === 'grid') {
|
|
349
|
-
// Issue #87:
|
|
350
|
-
//
|
|
351
|
-
//
|
|
352
|
-
//
|
|
444
|
+
// Issue #87 + #111: group-banded grid. Nodes are sorted by alias so
|
|
445
|
+
// same-prefix aliases are adjacent; each multi-member prefix group then
|
|
446
|
+
// gets its OWN row(s) starting at column 0, while singletons pack into
|
|
447
|
+
// shared rows. Group-banded placement keeps every group's bounding box
|
|
448
|
+
// (#111) a clean rectangle — no box ever overlaps another group's.
|
|
353
449
|
const all = [...online, ...offline];
|
|
354
|
-
const
|
|
355
|
-
const cols = Math.max(1, Math.ceil(Math.sqrt(
|
|
356
|
-
|
|
357
|
-
|
|
450
|
+
const groupKeys = computeGroups(all);
|
|
451
|
+
const cols = Math.max(1, Math.ceil(Math.sqrt(all.length)));
|
|
452
|
+
// #112: gy0 starts below the (now compact) recent-signal / legend
|
|
453
|
+
// overlay panels — their max bottom edge is y≈112 — so grid row 0 is
|
|
454
|
+
// never occluded; gy1 extends near the canvas bottom for breathing room.
|
|
455
|
+
const gx0 = 150, gx1 = 850, gy0 = 126, gy1 = 652;
|
|
358
456
|
const cellW = (gx1 - gx0) / cols;
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
457
|
+
// largest node radius at the current size scale — drives the group
|
|
458
|
+
// label band + the row-height floor so nothing ever overlaps.
|
|
459
|
+
const nodeR = Math.round(26 * nodeScale);
|
|
460
|
+
|
|
461
|
+
// ordered runs of consecutive same-group-key nodes (≥2 = real group)
|
|
462
|
+
const runs: { key: string; members: Session[] }[] = [];
|
|
463
|
+
for (const s of all) {
|
|
464
|
+
const gk = groupKeys[s.alias];
|
|
465
|
+
const last = runs[runs.length - 1];
|
|
466
|
+
if (last && last.key === gk) last.members.push(s);
|
|
467
|
+
else runs.push({ key: gk, members: [s] });
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// Pass 1 — assign each run to a band: a multi-member group owns its
|
|
471
|
+
// rows (left-aligned, so its bounding box is a tidy rect); contiguous
|
|
472
|
+
// singletons pack into shared rows (centred). Collect total row count.
|
|
473
|
+
type Band = { members: Session[]; startRow: number; centred: boolean; isGroup: boolean };
|
|
474
|
+
const bands: Band[] = [];
|
|
475
|
+
let row = 0;
|
|
476
|
+
let i = 0;
|
|
477
|
+
while (i < runs.length) {
|
|
478
|
+
if (runs[i].members.length >= 2) {
|
|
479
|
+
bands.push({ members: runs[i].members, startRow: row, centred: false, isGroup: true });
|
|
480
|
+
row += Math.ceil(runs[i].members.length / cols);
|
|
481
|
+
i++;
|
|
482
|
+
} else {
|
|
483
|
+
const singles: Session[] = [];
|
|
484
|
+
while (i < runs.length && runs[i].members.length < 2) {
|
|
485
|
+
singles.push(runs[i].members[0]);
|
|
486
|
+
i++;
|
|
487
|
+
}
|
|
488
|
+
bands.push({ members: singles, startRow: row, centred: true, isGroup: false });
|
|
489
|
+
row += Math.ceil(singles.length / cols);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
const totalRows = Math.max(1, row);
|
|
493
|
+
// #112: the group label sits in a band ABOVE the topmost node, so the
|
|
494
|
+
// band must clear the node radius — GROUP_TOP is node-relative, never
|
|
495
|
+
// cellH-derived (cellH-derived was the label↔node overlap bug Vincent
|
|
496
|
+
// hit). cellH then floors at GROUP_TOP + 30 so the label band + bottom
|
|
497
|
+
// padding always fit and stacked group boxes never touch; past the
|
|
498
|
+
// floor the grid overflows and zoom/pan handles it.
|
|
499
|
+
const GROUP_TOP = nodeR + 20;
|
|
500
|
+
// Row-height floor = the tightest of: a node + its label below it
|
|
501
|
+
// (2·nodeR + ~22), and the label band + min box padding (GROUP_TOP + 12).
|
|
502
|
+
// Below this, rows would overlap; past it the grid overflows and
|
|
503
|
+
// zoom/pan covers the rest.
|
|
504
|
+
const cellH = Math.max(2 * nodeR + 22, GROUP_TOP + 12, Math.min(100, (gy1 - gy0) / totalRows));
|
|
505
|
+
|
|
506
|
+
// Pass 2 — place each band's members.
|
|
507
|
+
for (const band of bands) {
|
|
508
|
+
band.members.forEach((s, idx) => {
|
|
509
|
+
const rowInBand = Math.floor(idx / cols);
|
|
510
|
+
const c = idx % cols;
|
|
511
|
+
const inRow = Math.min(cols, band.members.length - rowInBand * cols);
|
|
512
|
+
const inset = band.centred ? ((cols - inRow) * cellW) / 2 : 0;
|
|
513
|
+
positions[s.alias] = {
|
|
514
|
+
x: gx0 + inset + (c + 0.5) * cellW,
|
|
515
|
+
y: gy0 + (band.startRow + rowInBand + 0.5) * cellH,
|
|
516
|
+
};
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
|
|
370
520
|
const links = buildFlowLinks(messages, positions);
|
|
371
521
|
const active = new Set<string>();
|
|
372
522
|
links.forEach(link => { active.add(link.from); active.add(link.to); });
|
|
373
|
-
|
|
523
|
+
// #111: one bounding box per multi-member group (Vincent 4722). Each
|
|
524
|
+
// group owns its rows; GROUP_PAD fills the row space left below the
|
|
525
|
+
// nodes after the label band, so stacked group boxes always have a
|
|
526
|
+
// gap between them (GROUP_TOP is defined above, with cellH).
|
|
527
|
+
const GROUP_PAD = Math.max(8, Math.min(26, cellH - GROUP_TOP - 8)); // side/bottom
|
|
528
|
+
const groupBoxes = bands
|
|
529
|
+
.filter(b => b.isGroup)
|
|
530
|
+
.map(band => {
|
|
531
|
+
const pts = band.members.map(s => positions[s.alias]).filter(Boolean);
|
|
532
|
+
const xs = pts.map(p => p.x);
|
|
533
|
+
const ys = pts.map(p => p.y);
|
|
534
|
+
const minX = Math.min(...xs), minY = Math.min(...ys);
|
|
535
|
+
return {
|
|
536
|
+
key: band.members.length ? groupKeys[band.members[0].alias] : '',
|
|
537
|
+
count: band.members.length,
|
|
538
|
+
x: minX - GROUP_PAD,
|
|
539
|
+
y: minY - GROUP_TOP,
|
|
540
|
+
w: Math.max(...xs) - minX + GROUP_PAD * 2,
|
|
541
|
+
h: Math.max(...ys) - minY + GROUP_TOP + GROUP_PAD,
|
|
542
|
+
};
|
|
543
|
+
});
|
|
374
544
|
return {
|
|
375
545
|
onlineNodes: online,
|
|
376
546
|
offlineNodes: offline,
|
|
@@ -378,6 +548,7 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
378
548
|
flowLinks: links,
|
|
379
549
|
activeAliases: active,
|
|
380
550
|
groupKeys,
|
|
551
|
+
groupBoxes,
|
|
381
552
|
};
|
|
382
553
|
}
|
|
383
554
|
|
|
@@ -448,7 +619,7 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
448
619
|
});
|
|
449
620
|
|
|
450
621
|
// Round 106 (issue #83): group key per alias → shared hue per team.
|
|
451
|
-
const groupKeys =
|
|
622
|
+
const groupKeys = computeGroups([...online, ...offline]);
|
|
452
623
|
|
|
453
624
|
return {
|
|
454
625
|
onlineNodes: online,
|
|
@@ -457,15 +628,36 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
457
628
|
flowLinks: links,
|
|
458
629
|
activeAliases: active,
|
|
459
630
|
groupKeys,
|
|
631
|
+
// #111: group boxes are a grid-layout feature only — radially scattered
|
|
632
|
+
// ring nodes can't be cleanly boxed. Ring keeps the #83 prefix hue.
|
|
633
|
+
groupBoxes: [] as { key: string; count: number; x: number; y: number; w: number; h: number }[],
|
|
460
634
|
};
|
|
461
|
-
}, [messages, sessions, sseSessions, layout]);
|
|
635
|
+
}, [messages, sessions, sseSessions, layout, nodeScale]);
|
|
462
636
|
|
|
463
637
|
const workingCount = onlineNodes.filter(s => s.status === 'working').length;
|
|
638
|
+
// Round 6 / Loop: vendor distribution for the header chip — at a glance
|
|
639
|
+
// "what's in the fleet" (A:5 M:2 O:8 书:12 …) without opening a node.
|
|
640
|
+
// Sorted by count desc; "unknown" vendors collapse into a "?" bucket.
|
|
641
|
+
const vendorDist = useMemo(() => {
|
|
642
|
+
const tally = new Map<string, { initial: string; count: number; color: string }>();
|
|
643
|
+
for (const s of [...onlineNodes, ...offlineNodes]) {
|
|
644
|
+
const v = vendorForModel(s.model);
|
|
645
|
+
const key = v.id === 'unknown' ? '?' : v.initial;
|
|
646
|
+
const cur = tally.get(key);
|
|
647
|
+
if (cur) cur.count++;
|
|
648
|
+
else tally.set(key, { initial: key, count: 1, color: v.mono.text });
|
|
649
|
+
}
|
|
650
|
+
return [...tally.values()].sort((a, b) => b.count - a.count);
|
|
651
|
+
}, [onlineNodes, offlineNodes]);
|
|
464
652
|
// Round 109 (Vincent 4582 P0): hover-gated labels above this node count
|
|
465
653
|
// so dense fleets show clean avatars instead of a wall of overlapping
|
|
466
654
|
// label cards. 16 ≈ where the triple-tier rings start to crowd.
|
|
467
655
|
const denseLayout = onlineNodes.length + offlineNodes.length > 16;
|
|
468
656
|
const [hoveredAlias, setHoveredAlias] = useState<string | null>(null);
|
|
657
|
+
// Round 8 / Loop: which group is currently focused. Nodes outside this
|
|
658
|
+
// group + other group boxes fade so the eye locks onto the team you're
|
|
659
|
+
// pointing at. Singletons use their own alias as the group key.
|
|
660
|
+
const hoveredGroup = hoveredAlias ? (groupKeys[hoveredAlias] ?? hoveredAlias) : null;
|
|
469
661
|
|
|
470
662
|
// --- Round 103 (issue #81): fullscreen + zoom + pan interaction layer ---
|
|
471
663
|
// DIY native (no d3 / svg-pan-zoom): wrap the topology content in a single
|
|
@@ -485,6 +677,25 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
485
677
|
// Issue #100: singleton chat popover. One alias at a time — clicking
|
|
486
678
|
// another node swaps the target and the conversation switches in place.
|
|
487
679
|
const [chatAlias, setChatAlias] = useState<string | null>(null);
|
|
680
|
+
// Round 14 / Loop: one-shot click ripple — fades outward from the clicked
|
|
681
|
+
// node so the click registers physically before the popover snaps in.
|
|
682
|
+
// Pairs with the Round 11 chat-focus ring: ripple expands, ring locks on.
|
|
683
|
+
// Keyed by ts so re-clicking the same node remounts the <circle> and the
|
|
684
|
+
// SMIL <animate> replays. Cleared 600ms after click (longer than the
|
|
685
|
+
// 500ms animation so a final frame at opacity 0 is still in the tree).
|
|
686
|
+
const [clickRipple, setClickRipple] = useState<{
|
|
687
|
+
ts: number; x: number; y: number; r0: number; color: string;
|
|
688
|
+
} | null>(null);
|
|
689
|
+
// #84: when a node is renamed while its chat popover is open, follow the
|
|
690
|
+
// rename so the conversation keeps targeting a live alias.
|
|
691
|
+
useEffect(() => {
|
|
692
|
+
if (renameSignal && renameSignal.from === chatAlias) {
|
|
693
|
+
setChatAlias(renameSignal.to);
|
|
694
|
+
}
|
|
695
|
+
// chatAlias intentionally omitted — only react to a new rename signal,
|
|
696
|
+
// not to the user opening/closing the popover.
|
|
697
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
698
|
+
}, [renameSignal]);
|
|
488
699
|
|
|
489
700
|
useEffect(() => { viewRef.current = view; }, [view]);
|
|
490
701
|
|
|
@@ -524,6 +735,14 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
524
735
|
const svg = svgRef.current;
|
|
525
736
|
if (!svg) return;
|
|
526
737
|
const onWheel = (e: WheelEvent) => {
|
|
738
|
+
// Round 23 / Loop: only zoom on Ctrl/Meta+wheel when inline; plain
|
|
739
|
+
// wheel over the canvas keeps scrolling the page (the topo sits at
|
|
740
|
+
// the top of /; trapping page scroll mid-page is the classic
|
|
741
|
+
// "scroll-jail" anti-pattern). In fullscreen the canvas owns the
|
|
742
|
+
// viewport so any wheel zooms — no page scroll to preserve. Pinch-
|
|
743
|
+
// zoom on a trackpad surfaces as ctrlKey=true natively, which is
|
|
744
|
+
// also what we want (zoom the canvas, not the browser).
|
|
745
|
+
if (!isFullscreen && !e.ctrlKey && !e.metaKey) return;
|
|
527
746
|
e.preventDefault();
|
|
528
747
|
const rect = svg.getBoundingClientRect();
|
|
529
748
|
const mx = ((e.clientX - rect.left) / rect.width) * VIEWBOX_W;
|
|
@@ -544,8 +763,17 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
544
763
|
};
|
|
545
764
|
svg.addEventListener('wheel', onWheel, { passive: false });
|
|
546
765
|
return () => svg.removeEventListener('wheel', onWheel);
|
|
547
|
-
|
|
548
|
-
|
|
766
|
+
// Re-attach when the fullscreen flag toggles so the handler's
|
|
767
|
+
// closure picks up the new value (capture-by-closure means we'd
|
|
768
|
+
// otherwise read the stale boolean forever).
|
|
769
|
+
}, [isFullscreen]);
|
|
770
|
+
|
|
771
|
+
// Round 21 / Loop: pan-aware cursor. Static `grab` gave no tactile cue
|
|
772
|
+
// that the canvas was actually being dragged — "grabbing" while
|
|
773
|
+
// dragRef.current.active = true tells the hand it's moving the
|
|
774
|
+
// viewport. Mirroring dragRef.active into state is the only way to
|
|
775
|
+
// re-render the SVG style; the ref itself doesn't trigger renders.
|
|
776
|
+
const [isPanning, setIsPanning] = useState(false);
|
|
549
777
|
const onPointerDown = (e: React.PointerEvent<SVGSVGElement>) => {
|
|
550
778
|
if (e.button !== 0) return;
|
|
551
779
|
(e.currentTarget as Element).setPointerCapture?.(e.pointerId);
|
|
@@ -556,6 +784,7 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
556
784
|
baseX: viewRef.current.x,
|
|
557
785
|
baseY: viewRef.current.y,
|
|
558
786
|
};
|
|
787
|
+
setIsPanning(true);
|
|
559
788
|
};
|
|
560
789
|
const onPointerMove = (e: React.PointerEvent<SVGSVGElement>) => {
|
|
561
790
|
const d = dragRef.current;
|
|
@@ -570,6 +799,7 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
570
799
|
const onPointerUp = (e: React.PointerEvent<SVGSVGElement>) => {
|
|
571
800
|
if (!dragRef.current.active) return;
|
|
572
801
|
dragRef.current.active = false;
|
|
802
|
+
setIsPanning(false);
|
|
573
803
|
try {
|
|
574
804
|
(e.currentTarget as Element).releasePointerCapture?.(e.pointerId);
|
|
575
805
|
} catch {}
|
|
@@ -587,6 +817,30 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
587
817
|
};
|
|
588
818
|
const resetView = () => setView({ zoom: 1, x: 0, y: 0 });
|
|
589
819
|
|
|
820
|
+
// Round 22 / Loop: keyboard zoom — +/= zoom in, - zoom out, 0 reset.
|
|
821
|
+
// Listen on window so the user doesn't need to focus the SVG first,
|
|
822
|
+
// but only act when no text input has focus (otherwise typing "-" in
|
|
823
|
+
// a chat would zoom the topology — surprising and bad). Modifier keys
|
|
824
|
+
// pass through so Cmd/Ctrl combos still hit their owners. The button
|
|
825
|
+
// titles below document these shortcuts so users discover them.
|
|
826
|
+
useEffect(() => {
|
|
827
|
+
const onKey = (e: KeyboardEvent) => {
|
|
828
|
+
if (e.ctrlKey || e.metaKey || e.altKey) return;
|
|
829
|
+
const ae = document.activeElement as HTMLElement | null;
|
|
830
|
+
if (ae) {
|
|
831
|
+
const tag = ae.tagName;
|
|
832
|
+
if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT' || ae.isContentEditable) return;
|
|
833
|
+
}
|
|
834
|
+
if (e.key === '+' || e.key === '=') { zoomBy(1.2); e.preventDefault(); }
|
|
835
|
+
else if (e.key === '-' || e.key === '_') { zoomBy(1 / 1.2); e.preventDefault(); }
|
|
836
|
+
else if (e.key === '0') { resetView(); e.preventDefault(); }
|
|
837
|
+
};
|
|
838
|
+
window.addEventListener('keydown', onKey);
|
|
839
|
+
return () => window.removeEventListener('keydown', onKey);
|
|
840
|
+
// zoomBy / resetView are stable wrt setView callback; safe to omit deps
|
|
841
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
842
|
+
}, []);
|
|
843
|
+
|
|
590
844
|
const toggleFullscreen = () => {
|
|
591
845
|
const el = containerRef.current;
|
|
592
846
|
if (!el) return;
|
|
@@ -632,6 +886,19 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
632
886
|
<span className="px-2.5 py-1 rounded-md bg-cyan-500/10 text-cyan-300 border border-cyan-500/20">
|
|
633
887
|
{onlineNodes.length} online
|
|
634
888
|
</span>
|
|
889
|
+
{vendorDist.length > 1 && (
|
|
890
|
+
<span
|
|
891
|
+
className="inline-flex items-center gap-2 px-2.5 py-1 rounded-md bg-gray-500/10 text-gray-400 border border-gray-500/20 font-mono"
|
|
892
|
+
title="Fleet by model vendor"
|
|
893
|
+
>
|
|
894
|
+
{vendorDist.map(v => (
|
|
895
|
+
<span key={v.initial} className="inline-flex items-baseline gap-0.5">
|
|
896
|
+
<span style={{ color: v.color }}>{v.initial}</span>
|
|
897
|
+
<span className="text-gray-500">:{v.count}</span>
|
|
898
|
+
</span>
|
|
899
|
+
))}
|
|
900
|
+
</span>
|
|
901
|
+
)}
|
|
635
902
|
<span className="px-2.5 py-1 rounded-md bg-gray-500/10 text-gray-400 border border-gray-500/20">
|
|
636
903
|
{flowLinks.length} active links
|
|
637
904
|
</span>
|
|
@@ -654,7 +921,7 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
654
921
|
onPointerMove={onPointerMove}
|
|
655
922
|
onPointerUp={onPointerUp}
|
|
656
923
|
onPointerLeave={onPointerUp}
|
|
657
|
-
style={{ cursor: 'grab', touchAction: 'none' }}
|
|
924
|
+
style={{ cursor: isPanning ? 'grabbing' : 'grab', touchAction: 'none' }}
|
|
658
925
|
>
|
|
659
926
|
<defs>
|
|
660
927
|
<linearGradient id="topo-panel" x1="0" x2="1" y1="0" y2="1">
|
|
@@ -676,9 +943,37 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
676
943
|
</feMerge>
|
|
677
944
|
</filter>
|
|
678
945
|
)}
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
946
|
+
{/* Round 16 / Loop: 3-tier flow-link arrow markers.
|
|
947
|
+
The single marker had `markerUnits` defaulting to
|
|
948
|
+
`strokeWidth`, so heavy edges (stroke=7) rendered 35-user-
|
|
949
|
+
unit arrowheads — visually dominant, the head outweighed
|
|
950
|
+
the line. Switching to `userSpaceOnUse` decouples arrow
|
|
951
|
+
size from stroke; binning by count gives a clearer
|
|
952
|
+
hierarchy than continuous linear scaling:
|
|
953
|
+
s (count 1-2) → 12 user units
|
|
954
|
+
m (count 3-4) → 16 user units (alias for `topo-arrow`)
|
|
955
|
+
l (count 5+) → 22 user units
|
|
956
|
+
`topo-arrow` stays bound to the medium tier so the legend
|
|
957
|
+
swatch (line ~1500) renders without change. */}
|
|
958
|
+
{[
|
|
959
|
+
{ id: 'topo-arrow-s', size: 12 },
|
|
960
|
+
{ id: 'topo-arrow', size: 16 },
|
|
961
|
+
{ id: 'topo-arrow-l', size: 22 },
|
|
962
|
+
].map(m => (
|
|
963
|
+
<marker
|
|
964
|
+
key={m.id}
|
|
965
|
+
id={m.id}
|
|
966
|
+
viewBox="0 0 10 10"
|
|
967
|
+
refX="8"
|
|
968
|
+
refY="5"
|
|
969
|
+
markerWidth={m.size}
|
|
970
|
+
markerHeight={m.size}
|
|
971
|
+
markerUnits="userSpaceOnUse"
|
|
972
|
+
orient="auto-start-reverse"
|
|
973
|
+
>
|
|
974
|
+
<path d="M 0 0 L 10 5 L 0 10 z" fill={pal.arrowFill} />
|
|
975
|
+
</marker>
|
|
976
|
+
))}
|
|
682
977
|
{/* Round 45: radial radar sweep gradient — bright at center
|
|
683
978
|
(where it meets the hub) fading to leading edge. */}
|
|
684
979
|
<radialGradient id="topo-sweep" cx="0%" cy="50%" r="100%">
|
|
@@ -806,16 +1101,89 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
806
1101
|
);
|
|
807
1102
|
})}
|
|
808
1103
|
|
|
1104
|
+
{/* #111: prefix-group boundary boxes (Vincent 4722). Grid layout
|
|
1105
|
+
only — groupBoxes is empty in ring mode. Rendered behind the
|
|
1106
|
+
flow links + nodes; pointer-events off so they never intercept
|
|
1107
|
+
a node click. Restrained dashed container + group-name label. */}
|
|
1108
|
+
{groupBoxes.map(box => {
|
|
1109
|
+
const isHovered = hoveredGroup === box.key;
|
|
1110
|
+
// Round 18 / Loop: group-box hover linkage. The Round 8 fade
|
|
1111
|
+
// already dropped OUT-of-focus groups to 0.28, but the IN-focus
|
|
1112
|
+
// group sat at its baseline appearance — no positive emphasis.
|
|
1113
|
+
// Hovering now upgrades the box to an "accent" treatment:
|
|
1114
|
+
// solid stroke (not dashed), thicker, accent-coloured; brighter
|
|
1115
|
+
// text and slightly stronger fill. Label and box read as one
|
|
1116
|
+
// selected unit. Geometry unchanged → overlap test untouched.
|
|
1117
|
+
return (
|
|
1118
|
+
<g
|
|
1119
|
+
key={`grp-${box.key}`}
|
|
1120
|
+
data-group={box.key}
|
|
1121
|
+
className="transition-opacity"
|
|
1122
|
+
style={{ pointerEvents: 'none', opacity: !hoveredGroup || isHovered ? 1 : 0.28 }}
|
|
1123
|
+
>
|
|
1124
|
+
<rect
|
|
1125
|
+
x={box.x}
|
|
1126
|
+
y={box.y}
|
|
1127
|
+
width={box.w}
|
|
1128
|
+
height={box.h}
|
|
1129
|
+
rx="14"
|
|
1130
|
+
fill={isLight ? '#0f172a' : '#a5b4fc'}
|
|
1131
|
+
fillOpacity={isHovered ? (isLight ? 0.05 : 0.09) : (isLight ? 0.025 : 0.045)}
|
|
1132
|
+
stroke={isHovered ? pal.legendAccent : pal.ringStroke}
|
|
1133
|
+
strokeWidth={isHovered ? 2 : 1.5}
|
|
1134
|
+
strokeDasharray={isHovered ? 'none' : '6 6'}
|
|
1135
|
+
style={{ transition: 'stroke 200ms ease-out, stroke-width 200ms ease-out, fill-opacity 200ms ease-out' }}
|
|
1136
|
+
/>
|
|
1137
|
+
<text
|
|
1138
|
+
x={box.x + 12}
|
|
1139
|
+
y={box.y + 14}
|
|
1140
|
+
fill={isHovered ? pal.legendHeadline : pal.legendText}
|
|
1141
|
+
fontSize="13"
|
|
1142
|
+
fontFamily="monospace"
|
|
1143
|
+
fontWeight="700"
|
|
1144
|
+
style={{ transition: 'fill 200ms ease-out' }}
|
|
1145
|
+
>
|
|
1146
|
+
{box.key}
|
|
1147
|
+
{/* Round 19 / Loop: member-count chip. Inline tspan stays
|
|
1148
|
+
in the single <text> bbox the overlap test reads, so
|
|
1149
|
+
the node↔label guard still catches if the chip ever
|
|
1150
|
+
pushes the label far enough right to clip a node.
|
|
1151
|
+
Smaller + lighter weight reads as metadata, not name. */}
|
|
1152
|
+
<tspan dx="6" fill={pal.legendText} fontSize="11" fontWeight="400">· {box.count}</tspan>
|
|
1153
|
+
</text>
|
|
1154
|
+
</g>
|
|
1155
|
+
);
|
|
1156
|
+
})}
|
|
1157
|
+
|
|
809
1158
|
{/* directed message flows */}
|
|
810
1159
|
{flowLinks.map((link, index) => {
|
|
811
1160
|
const from = nodePositions[link.from];
|
|
812
1161
|
const to = nodePositions[link.to];
|
|
813
1162
|
if (!from || !to) return null;
|
|
814
1163
|
|
|
815
|
-
|
|
1164
|
+
// Round 7 / Loop: lift now scales with distance so short links
|
|
1165
|
+
// aren't over-bent (long links keep the ~36px hump), and the
|
|
1166
|
+
// particle period shortens with link.count so busier edges
|
|
1167
|
+
// visibly pulse faster — instant info density on top of the
|
|
1168
|
+
// existing stroke-width-by-count chip.
|
|
1169
|
+
const dist = Math.hypot(to.x - from.x, to.y - from.y);
|
|
1170
|
+
const lift = (index % 2 === 0 ? 1 : -1) * Math.min(36, dist * 0.18);
|
|
816
1171
|
const path = curvePath(from, to, lift);
|
|
817
1172
|
const width = Math.min(2 + link.count, 7);
|
|
818
|
-
const duration = 2.
|
|
1173
|
+
const duration = Math.max(0.9, 2.6 / Math.sqrt(link.count));
|
|
1174
|
+
// Round 10 / Loop: freshness fade. An edge that fired ≤30s ago
|
|
1175
|
+
// stays at full intensity; over 5 minutes it decays to ~35%.
|
|
1176
|
+
// Surfaces "what's happening now" vs background chatter without
|
|
1177
|
+
// hiding old flow entirely (some context still useful). `now`
|
|
1178
|
+
// captured at useMemo-recompute time (every 5s message refresh)
|
|
1179
|
+
// — accuracy is within the poll interval, plenty.
|
|
1180
|
+
const ageMs = link.last_at ? Math.max(0, Date.now() - Date.parse(link.last_at)) : 0;
|
|
1181
|
+
const fresh = Math.max(0.35, 1 - ageMs / (5 * 60 * 1000));
|
|
1182
|
+
// Round 16 arrow-tier binning — keep `topo-arrow` as the
|
|
1183
|
+
// medium tier id so the legend swatch picks it up unchanged.
|
|
1184
|
+
const arrowId = link.count <= 2 ? 'topo-arrow-s'
|
|
1185
|
+
: link.count <= 4 ? 'topo-arrow'
|
|
1186
|
+
: 'topo-arrow-l';
|
|
819
1187
|
|
|
820
1188
|
return (
|
|
821
1189
|
<g key={link.key}>
|
|
@@ -824,9 +1192,10 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
824
1192
|
fill="none"
|
|
825
1193
|
stroke={pal.flowEdge}
|
|
826
1194
|
strokeWidth={width}
|
|
827
|
-
opacity={isLight ? 0.22 : 0.28}
|
|
1195
|
+
opacity={(isLight ? 0.22 : 0.28) * fresh}
|
|
828
1196
|
filter={isLight ? undefined : 'url(#topo-glow)'}
|
|
829
|
-
markerEnd=
|
|
1197
|
+
markerEnd={`url(#${arrowId})`}
|
|
1198
|
+
className="transition-opacity duration-500"
|
|
830
1199
|
/>
|
|
831
1200
|
<path
|
|
832
1201
|
id={`flow-path-${index}`}
|
|
@@ -835,37 +1204,37 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
835
1204
|
stroke={pal.flowPath}
|
|
836
1205
|
strokeWidth="1"
|
|
837
1206
|
strokeDasharray="2 12"
|
|
838
|
-
opacity={isLight ? 0.4 : 0.75}
|
|
1207
|
+
opacity={(isLight ? 0.4 : 0.75) * fresh}
|
|
1208
|
+
className="transition-opacity duration-500"
|
|
839
1209
|
/>
|
|
840
|
-
<circle r="4" fill={pal.flowParticle} filter={isLight ? undefined : 'url(#topo-glow)'}>
|
|
1210
|
+
<circle r="4" fill={pal.flowParticle} filter={isLight ? undefined : 'url(#topo-glow)'} opacity={fresh}>
|
|
841
1211
|
<animateMotion dur={`${duration}s`} repeatCount="indefinite" path={path} />
|
|
842
1212
|
</circle>
|
|
843
1213
|
</g>
|
|
844
1214
|
);
|
|
845
1215
|
})}
|
|
846
1216
|
|
|
847
|
-
{/* center hub — round 39
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
the
|
|
1217
|
+
{/* center hub — round 39 sized + round 13 restraint.
|
|
1218
|
+
The hub is the control-plane anchor. r39 gave it two outward
|
|
1219
|
+
pulses (r 10→38, 2.4s, double-phase) which read as "loudest
|
|
1220
|
+
node in the room" — wrong semantics: anchors don't emit,
|
|
1221
|
+
they hold. r13 swaps the kinetic pulse for a slow opacity
|
|
1222
|
+
breath on the static halo (4s, ±15% from base), so the hub
|
|
1223
|
+
stays alive without throwing kinetic energy outward. The
|
|
1224
|
+
dual-circle "lit lamp" core is unchanged. */}
|
|
853
1225
|
{layout === 'ring' && (<g>
|
|
854
|
-
{/*
|
|
1226
|
+
{/* grounding halo — now breathes in opacity, no expansion */}
|
|
855
1227
|
<circle
|
|
856
1228
|
cx={cx} cy={cy} r="18"
|
|
857
1229
|
fill={isLight ? '#d1fae5' : '#10b981'}
|
|
858
|
-
opacity={isLight ? 0.
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
<circle cx={cx} cy={cy} r="10" fill="none" stroke="#10b981" strokeWidth="1.5" opacity="0">
|
|
867
|
-
<animate attributeName="r" values="10;38;10" dur="2.4s" begin="1.2s" repeatCount="indefinite" />
|
|
868
|
-
<animate attributeName="opacity" values="0.45;0;0.45" dur="2.4s" begin="1.2s" repeatCount="indefinite" />
|
|
1230
|
+
opacity={isLight ? 0.42 : 0.12}
|
|
1231
|
+
>
|
|
1232
|
+
<animate
|
|
1233
|
+
attributeName="opacity"
|
|
1234
|
+
values={isLight ? '0.32;0.52;0.32' : '0.08;0.16;0.08'}
|
|
1235
|
+
dur="4s"
|
|
1236
|
+
repeatCount="indefinite"
|
|
1237
|
+
/>
|
|
869
1238
|
</circle>
|
|
870
1239
|
{/* core — 20px diameter, larger inner highlight reads as a "lit lamp" */}
|
|
871
1240
|
<circle cx={cx} cy={cy} r="10" fill={isLight ? '#059669' : '#10b981'} />
|
|
@@ -873,7 +1242,7 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
873
1242
|
</g>)}
|
|
874
1243
|
|
|
875
1244
|
{/* agent nodes */}
|
|
876
|
-
{[...onlineNodes, ...offlineNodes].map(session => {
|
|
1245
|
+
{[...onlineNodes, ...offlineNodes].map((session, nodeIdx) => {
|
|
877
1246
|
const pos = nodePositions[session.alias];
|
|
878
1247
|
if (!pos) return null;
|
|
879
1248
|
|
|
@@ -881,7 +1250,9 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
881
1250
|
const isOnline = session.status !== 'offline' || !!sseCountFor;
|
|
882
1251
|
const status = nodeStatus(session, isOnline, isLight);
|
|
883
1252
|
const isActive = activeAliases.has(session.alias);
|
|
884
|
-
|
|
1253
|
+
// #113: node size scales with the S/M/L control; halos, labels,
|
|
1254
|
+
// badge and avatar all derive from `radius` so they follow.
|
|
1255
|
+
const radius = Math.round((isOnline ? 26 : 18) * nodeScale);
|
|
885
1256
|
// Round 109/110 (Vincent 4582 + 4583 P0): at high node counts
|
|
886
1257
|
// the 100px opaque label cards overlapped each other and
|
|
887
1258
|
// covered neighbouring avatars. But hiding text entirely went
|
|
@@ -891,12 +1262,48 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
891
1262
|
// occlude an avatar), and the full card appears only for the
|
|
892
1263
|
// hovered node or once zoomed in past 1.4×.
|
|
893
1264
|
const showFullLabel = !denseLayout || hoveredAlias === session.alias || view.zoom >= 1.4;
|
|
1265
|
+
// Round 8: when a node in another group is hovered, fade this
|
|
1266
|
+
// one. Same-group nodes (incl. singletons matching the hovered
|
|
1267
|
+
// alias) stay full. Pure visual focus, geometry unchanged.
|
|
1268
|
+
const inFocus = !hoveredGroup || (groupKeys[session.alias] ?? session.alias) === hoveredGroup;
|
|
894
1269
|
|
|
895
1270
|
return (
|
|
896
1271
|
<g
|
|
897
1272
|
key={session.alias}
|
|
898
|
-
|
|
899
|
-
|
|
1273
|
+
data-node={session.alias}
|
|
1274
|
+
// Round 3 / Loop: `anet-fade-in` runs once when the <g>
|
|
1275
|
+
// mounts — a new session entering the fleet (or the topology
|
|
1276
|
+
// first rendering) eases in instead of popping. Re-renders of
|
|
1277
|
+
// an existing node don't re-trigger (React preserves the <g>
|
|
1278
|
+
// via the alias key), so status changes don't flicker. The
|
|
1279
|
+
// global prefers-reduced-motion sweep already neutralises it.
|
|
1280
|
+
className="group transition-opacity anet-fade-in"
|
|
1281
|
+
style={{
|
|
1282
|
+
cursor: 'pointer',
|
|
1283
|
+
// Round 17 / Loop: offline nodes drop to 0.6 at rest so
|
|
1284
|
+
// online nodes pop without losing the offline-as-ghost
|
|
1285
|
+
// information. The dashed stroke + smaller radius already
|
|
1286
|
+
// say "offline"; dimming the whole group strengthens the
|
|
1287
|
+
// online-vs-offline hierarchy at first glance. Exempt the
|
|
1288
|
+
// chat-focused node — if the user explicitly opened a
|
|
1289
|
+
// popover targeting an offline alias, that node stays
|
|
1290
|
+
// full-brightness so the focus ring + popover read as one
|
|
1291
|
+
// selected thing rather than a dimmed selection. Group-
|
|
1292
|
+
// hover fade (Round 8) still wins when inFocus is false.
|
|
1293
|
+
opacity: !inFocus
|
|
1294
|
+
? 0.32
|
|
1295
|
+
: chatAlias === session.alias
|
|
1296
|
+
? 1
|
|
1297
|
+
: isOnline ? 1 : 0.6,
|
|
1298
|
+
// Round 9 / Loop: stagger the anet-fade-in so the topology
|
|
1299
|
+
// reveals as a wave on first paint instead of one big pop.
|
|
1300
|
+
// Cap at 24 indices (≈600ms tail) so 50-node fleets still
|
|
1301
|
+
// finish revealing within a beat. CSS animation-delay only
|
|
1302
|
+
// applies during the keyframe — re-renders without a new
|
|
1303
|
+
// mount (same alias key) don't replay, so status changes
|
|
1304
|
+
// never trigger the stagger again.
|
|
1305
|
+
animationDelay: `${Math.min(nodeIdx, 24) * 25}ms`,
|
|
1306
|
+
}}
|
|
900
1307
|
// Stop the pointerdown from reaching the SVG pan handler: the
|
|
901
1308
|
// SVG calls setPointerCapture, and a captured pointer makes
|
|
902
1309
|
// Chromium fire the follow-up `click` on the SVG instead of
|
|
@@ -904,17 +1311,86 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
904
1311
|
// Side effect (intended): you pan from empty canvas, not by
|
|
905
1312
|
// grabbing a node.
|
|
906
1313
|
onPointerDown={(e) => e.stopPropagation()}
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
1314
|
+
// Track hover globally (not just under denseLayout) so the
|
|
1315
|
+
// Round 8 group-focus fade works at any fleet size.
|
|
1316
|
+
onPointerEnter={() => setHoveredAlias(session.alias)}
|
|
1317
|
+
onPointerLeave={() => setHoveredAlias(prev => (prev === session.alias ? null : prev))}
|
|
1318
|
+
onClick={() => {
|
|
1319
|
+
setChatAlias(session.alias);
|
|
1320
|
+
// Round 14 ripple — capture position, radius and status
|
|
1321
|
+
// colour at click time so the one-shot circle is self-
|
|
1322
|
+
// contained (no re-render-time recomputation needed).
|
|
1323
|
+
setClickRipple({
|
|
1324
|
+
ts: Date.now(),
|
|
1325
|
+
x: pos.x, y: pos.y, r0: radius,
|
|
1326
|
+
color: status.primary,
|
|
1327
|
+
});
|
|
1328
|
+
setTimeout(() => setClickRipple(prev =>
|
|
1329
|
+
prev && Date.now() - prev.ts >= 590 ? null : prev), 600);
|
|
1330
|
+
}}
|
|
910
1331
|
>
|
|
1332
|
+
{/* Issue #96: native hover tooltip — "Vendor · model · Runtime".
|
|
1333
|
+
Falls back to just the alias when the node reports no
|
|
1334
|
+
model/runtime. */}
|
|
1335
|
+
<title>{identityLine(session.model, session.runtime) || session.alias}</title>
|
|
1336
|
+
{/* Round 2 / Loop: hover ring — a thin outer stroke that fades
|
|
1337
|
+
in when the cursor enters the node, signalling clickability
|
|
1338
|
+
(real-user feedback for the chat-popover open). Pure CSS via
|
|
1339
|
+
Tailwind group-hover, so it costs nothing per frame and
|
|
1340
|
+
respects prefers-reduced-motion via the global media query. */}
|
|
1341
|
+
<circle
|
|
1342
|
+
cx={pos.x}
|
|
1343
|
+
cy={pos.y}
|
|
1344
|
+
r={radius + 12}
|
|
1345
|
+
fill="none"
|
|
1346
|
+
stroke={status.primary}
|
|
1347
|
+
// strokeWidth must NOT be 1.5 (offline status ring) or 3
|
|
1348
|
+
// (online status ring) — the overlap test selects by those
|
|
1349
|
+
// exact widths and would mis-count this invisible hover
|
|
1350
|
+
// ring as a node footprint.
|
|
1351
|
+
strokeWidth="2"
|
|
1352
|
+
className="opacity-0 group-hover:opacity-70 transition-opacity duration-150"
|
|
1353
|
+
style={{ pointerEvents: 'none' }}
|
|
1354
|
+
/>
|
|
1355
|
+
{/* Round 11 / Loop: chat-focus ring — when the ChatPopover is
|
|
1356
|
+
open targeting this node, anchor a persistent ring around
|
|
1357
|
+
it so the floating popover visibly links back to its source
|
|
1358
|
+
node. Static (not pulsing) so it reads as "selected state"
|
|
1359
|
+
rather than "this node is active". strokeWidth=2.5 stays
|
|
1360
|
+
clear of the overlap-test selectors (1.5 / 3). Sits just
|
|
1361
|
+
outside the halo radius+8 so it never overlaps a neighbour
|
|
1362
|
+
(halos already pack flush in dense grids). */}
|
|
1363
|
+
{chatAlias === session.alias && (
|
|
1364
|
+
<circle
|
|
1365
|
+
cx={pos.x}
|
|
1366
|
+
cy={pos.y}
|
|
1367
|
+
r={radius + 14}
|
|
1368
|
+
fill="none"
|
|
1369
|
+
stroke={status.primary}
|
|
1370
|
+
strokeWidth="2.5"
|
|
1371
|
+
opacity={isLight ? 0.85 : 0.95}
|
|
1372
|
+
filter={!isLight ? 'url(#topo-glow)' : undefined}
|
|
1373
|
+
className="transition-opacity duration-200"
|
|
1374
|
+
style={{ pointerEvents: 'none' }}
|
|
1375
|
+
/>
|
|
1376
|
+
)}
|
|
911
1377
|
{isActive && (
|
|
912
1378
|
<circle cx={pos.x} cy={pos.y} r={radius + 14} fill={status.primary} opacity={isLight ? 0.08 : 0.12}>
|
|
913
1379
|
<animate attributeName="r" values={`${radius + 8};${radius + 22};${radius + 8}`} dur="2.4s" repeatCount="indefinite" />
|
|
914
1380
|
<animate attributeName="opacity" values={isLight ? '0.12;0.02;0.12' : '0.18;0.04;0.18'} dur="2.4s" repeatCount="indefinite" />
|
|
915
1381
|
</circle>
|
|
916
1382
|
)}
|
|
917
|
-
|
|
1383
|
+
{/* Round 4 / Loop: transition-[fill,stroke,opacity] smooths
|
|
1384
|
+
status colour changes so idle↔working↔offline doesn't snap
|
|
1385
|
+
— task replies / node-rename / SSE updates ease in. */}
|
|
1386
|
+
<circle
|
|
1387
|
+
cx={pos.x}
|
|
1388
|
+
cy={pos.y}
|
|
1389
|
+
r={radius + 8}
|
|
1390
|
+
fill={status.halo}
|
|
1391
|
+
opacity={isOnline ? (isLight ? 0.85 : 0.65) : (isLight ? 0.4 : 0.25)}
|
|
1392
|
+
className="transition-[fill,opacity] duration-300 ease-out"
|
|
1393
|
+
/>
|
|
918
1394
|
<circle
|
|
919
1395
|
cx={pos.x}
|
|
920
1396
|
cy={pos.y}
|
|
@@ -924,34 +1400,28 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
924
1400
|
strokeWidth={isOnline ? 3 : 1.5}
|
|
925
1401
|
strokeDasharray={isOnline ? 'none' : '5 5'}
|
|
926
1402
|
filter={isOnline && !isLight ? 'url(#topo-glow)' : undefined}
|
|
1403
|
+
className="transition-[fill,stroke] duration-300 ease-out"
|
|
927
1404
|
/>
|
|
928
|
-
{/*
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
1405
|
+
{/* Issue #96: node "avatar" is now driven by the model
|
|
1406
|
+
vendor. Decision order:
|
|
1407
|
+
1. ?brand=intern flag, or an intern-aliased node with
|
|
1408
|
+
no model field → 书生 coin (preserves #79).
|
|
1409
|
+
2. vendor has a packaged logo asset → that logo image
|
|
1410
|
+
(intern-s1-* models land here via vendorForModel).
|
|
1411
|
+
3. known vendor, logo asset not shipped yet → a
|
|
1412
|
+
vendor-tinted monogram (spec-mandated fallback).
|
|
1413
|
+
4. unknown vendor / null model → the prefix-group
|
|
1414
|
+
hue-hashed initial (#83/#99 behaviour, unchanged). */}
|
|
935
1415
|
{(() => {
|
|
936
|
-
const ar = isOnline ? 14 : 10;
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
// forces the coin on every node (full brand showcase).
|
|
943
|
-
const isInternNode = isIntern || /书生|书小生|intern/i.test(session.alias);
|
|
944
|
-
if (isInternNode) {
|
|
945
|
-
// Round 102: self-contained "avatar coin" — 书小生 figure
|
|
946
|
-
// keyed off its white background and composited onto a
|
|
947
|
-
// cream circular backplate. The cream coin gives the
|
|
948
|
-
// dark-haired figure consistent contrast on BOTH the
|
|
949
|
-
// dark cyber theme and light theme. Render at the node
|
|
950
|
-
// diameter so the coin fills inside the status ring.
|
|
951
|
-
const size = radius * 2;
|
|
1416
|
+
const ar = Math.round((isOnline ? 14 : 10) * nodeScale);
|
|
1417
|
+
const size = radius * 2;
|
|
1418
|
+
const vendor = vendorForModel(session.model);
|
|
1419
|
+
const internByAlias = /书生|书小生|intern/i.test(session.alias);
|
|
1420
|
+
|
|
1421
|
+
if (isIntern || internByAlias || vendor.logo) {
|
|
952
1422
|
return (
|
|
953
1423
|
<image
|
|
954
|
-
href=
|
|
1424
|
+
href={vendor.logo ?? '/intern_avatar.png'}
|
|
955
1425
|
x={pos.x - size / 2}
|
|
956
1426
|
y={pos.y - size / 2}
|
|
957
1427
|
width={size}
|
|
@@ -960,12 +1430,25 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
960
1430
|
/>
|
|
961
1431
|
);
|
|
962
1432
|
}
|
|
1433
|
+
if (vendor.id !== 'unknown') {
|
|
1434
|
+
// Known model house, logo asset not in public/vendors/
|
|
1435
|
+
// yet — vendor-tinted monogram stands in.
|
|
1436
|
+
return (
|
|
1437
|
+
<>
|
|
1438
|
+
<circle cx={pos.x} cy={pos.y} r={ar} fill={vendor.mono.bg} stroke={vendor.mono.ring} strokeWidth="1" />
|
|
1439
|
+
<text
|
|
1440
|
+
x={pos.x} y={pos.y} dy="0.34em" textAnchor="middle"
|
|
1441
|
+
fill={vendor.mono.text} fontSize={ar}
|
|
1442
|
+
fontFamily="monospace" fontWeight="700"
|
|
1443
|
+
>
|
|
1444
|
+
{vendor.initial}
|
|
1445
|
+
</text>
|
|
1446
|
+
</>
|
|
1447
|
+
);
|
|
1448
|
+
}
|
|
963
1449
|
// Round 106 (issue #83): hue keyed to the prefix group,
|
|
964
|
-
// not the full alias — every 通信* node shares one color
|
|
965
|
-
// every 研究员* another, so teams cluster visually even
|
|
966
|
-
// when the tier layout spreads them across rings.
|
|
1450
|
+
// not the full alias — every 通信* node shares one color.
|
|
967
1451
|
const c = aliasAvatarColors(groupKeys[session.alias] || session.alias);
|
|
968
|
-
const fs = isOnline ? 14 : 10;
|
|
969
1452
|
return (
|
|
970
1453
|
<>
|
|
971
1454
|
<circle cx={pos.x} cy={pos.y} r={ar} fill={c.bg} stroke={c.ring} strokeWidth="1" />
|
|
@@ -975,7 +1458,7 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
975
1458
|
dy="0.34em"
|
|
976
1459
|
textAnchor="middle"
|
|
977
1460
|
fill={c.text}
|
|
978
|
-
fontSize={
|
|
1461
|
+
fontSize={ar}
|
|
979
1462
|
fontFamily="monospace"
|
|
980
1463
|
fontWeight="700"
|
|
981
1464
|
>
|
|
@@ -984,12 +1467,32 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
984
1467
|
</>
|
|
985
1468
|
);
|
|
986
1469
|
})()}
|
|
1470
|
+
{/* Issue #96: runtime badge — small corner glyph marking the
|
|
1471
|
+
execution shell (CLI / SDK / HTTP API). Sits bottom-right
|
|
1472
|
+
of the avatar; colours kept off the working/idle/offline
|
|
1473
|
+
status hues. Absent when the node reports no runtime. */}
|
|
1474
|
+
{(() => {
|
|
1475
|
+
const rt = runtimeIdentity(session.runtime);
|
|
1476
|
+
if (!rt) return null;
|
|
1477
|
+
const br = isOnline ? 7 : 5.5;
|
|
1478
|
+
const bx = pos.x + radius * 0.72;
|
|
1479
|
+
const by = pos.y + radius * 0.72;
|
|
1480
|
+
const icon = br * 2 * 0.62;
|
|
1481
|
+
return (
|
|
1482
|
+
<g style={{ pointerEvents: 'none' }}>
|
|
1483
|
+
<circle cx={bx} cy={by} r={br} fill={pal.containerBg} stroke={rt.color} strokeWidth="1.5" />
|
|
1484
|
+
<g transform={`translate(${bx - icon / 2} ${by - icon / 2}) scale(${icon / 24})`}>
|
|
1485
|
+
<path d={rt.iconPath} fill="none" stroke={rt.color} strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round" />
|
|
1486
|
+
</g>
|
|
1487
|
+
</g>
|
|
1488
|
+
);
|
|
1489
|
+
})()}
|
|
987
1490
|
{session.status === 'working' && (
|
|
988
1491
|
// working pulse — small dot just inside the status ring,
|
|
989
1492
|
// above the avatar text. Was a 18px horizontal bar at
|
|
990
1493
|
// y+11 which now collides with the avatar; the dot reads
|
|
991
1494
|
// as the same "active" cue without occluding the initial.
|
|
992
|
-
<circle cx={pos.x} cy={pos.y - (
|
|
1495
|
+
<circle cx={pos.x} cy={pos.y - (radius - 6)} r="2.5" fill={pal.flowParticle}>
|
|
993
1496
|
<animate attributeName="opacity" values="1;0.25;1" dur="1.1s" repeatCount="indefinite" />
|
|
994
1497
|
</circle>
|
|
995
1498
|
)}
|
|
@@ -998,61 +1501,112 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
998
1501
|
Round 109/110 (Vincent P0): full opaque card below the
|
|
999
1502
|
density threshold / on hover / when zoomed; otherwise a
|
|
1000
1503
|
lightweight plain-text alias that keeps every node
|
|
1001
|
-
labelled without an opaque box covering its neighbours.
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1504
|
+
labelled without an opaque box covering its neighbours.
|
|
1505
|
+
Round 15 / Loop: when nodeScale=S the node shrinks 30%
|
|
1506
|
+
but the label card was staying full-size, so the label
|
|
1507
|
+
visually outweighed the small node. Tighten the card
|
|
1508
|
+
frame, alias / sub fontSize, drop-offset and truncate
|
|
1509
|
+
length specifically for S; M and L keep their existing
|
|
1510
|
+
sizes (M ≈ L for labels by design — the S user is the
|
|
1511
|
+
one who explicitly asked for a denser view). */}
|
|
1512
|
+
{(() => {
|
|
1513
|
+
const isSmall = nodeScale < 0.8;
|
|
1514
|
+
const cardW = isSmall ? 88 : 100;
|
|
1515
|
+
const cardH = isSmall ? 36 : 42;
|
|
1516
|
+
const cardTopY = isSmall ? -12 : -14;
|
|
1517
|
+
const aliasFs = isSmall ? 11 : 12;
|
|
1518
|
+
const subFs = isSmall ? 8 : 9;
|
|
1519
|
+
const subY = isSmall ? 15 : 17;
|
|
1520
|
+
const dropY = isSmall ? 18 : 22;
|
|
1521
|
+
const fullMax = isSmall ? 11 : 12;
|
|
1522
|
+
const denseFs = isSmall ? 9 : 10;
|
|
1523
|
+
const denseDrop = isSmall ? 12 : 14;
|
|
1524
|
+
return showFullLabel ? (
|
|
1525
|
+
<g transform={`translate(${pos.x}, ${pos.y + radius + dropY})`} style={{ pointerEvents: 'none' }}>
|
|
1526
|
+
<rect x={-cardW / 2} y={cardTopY} width={cardW} height={cardH} rx="6" fill={pal.labelBox.fill} stroke={pal.labelBox.stroke} opacity={isLight ? 1 : 0.94} />
|
|
1527
|
+
<text x="0" y="1" textAnchor="middle" fill={status.text} fontSize={aliasFs} fontFamily="monospace" fontWeight="700">
|
|
1528
|
+
{truncate(session.alias, fullMax)}
|
|
1529
|
+
</text>
|
|
1530
|
+
<text x="0" y={subY} textAnchor="middle" fill={status.primary} fontSize={subFs} fontFamily="monospace">
|
|
1531
|
+
{status.label}{isOnline && sseCountFor != null ? ` sse:${sseCountFor}` : ''}
|
|
1532
|
+
</text>
|
|
1533
|
+
</g>
|
|
1534
|
+
) : (
|
|
1535
|
+
<text
|
|
1536
|
+
x={pos.x}
|
|
1537
|
+
y={pos.y + radius + denseDrop}
|
|
1538
|
+
textAnchor="middle"
|
|
1539
|
+
fill={status.text}
|
|
1540
|
+
fontSize={denseFs}
|
|
1541
|
+
fontFamily="monospace"
|
|
1542
|
+
fontWeight="700"
|
|
1543
|
+
opacity={0.9}
|
|
1544
|
+
style={{ pointerEvents: 'none', paintOrder: 'stroke' }}
|
|
1545
|
+
stroke={pal.containerBg}
|
|
1546
|
+
strokeWidth="3"
|
|
1547
|
+
>
|
|
1548
|
+
{truncate(session.alias, isSmall ? 9 : 10)}
|
|
1010
1549
|
</text>
|
|
1011
|
-
|
|
1012
|
-
)
|
|
1013
|
-
<text
|
|
1014
|
-
x={pos.x}
|
|
1015
|
-
y={pos.y + radius + 14}
|
|
1016
|
-
textAnchor="middle"
|
|
1017
|
-
fill={status.text}
|
|
1018
|
-
fontSize="10"
|
|
1019
|
-
fontFamily="monospace"
|
|
1020
|
-
fontWeight="700"
|
|
1021
|
-
opacity={0.9}
|
|
1022
|
-
style={{ pointerEvents: 'none', paintOrder: 'stroke' }}
|
|
1023
|
-
stroke={pal.containerBg}
|
|
1024
|
-
strokeWidth="3"
|
|
1025
|
-
>
|
|
1026
|
-
{truncate(session.alias, 10)}
|
|
1027
|
-
</text>
|
|
1028
|
-
)}
|
|
1550
|
+
);
|
|
1551
|
+
})()}
|
|
1029
1552
|
</g>
|
|
1030
1553
|
);
|
|
1031
1554
|
})}
|
|
1032
1555
|
|
|
1556
|
+
{/* Round 14 / Loop: click ripple — one-shot expanding ring from
|
|
1557
|
+
the clicked node, ~500ms, status-coloured. Sits inside the
|
|
1558
|
+
zoom/pan <g> so it scales / pans with the topology. Keyed by
|
|
1559
|
+
ts so a re-click on any node (same or different) remounts the
|
|
1560
|
+
<circle> and the SMIL <animate> elements replay from t=0.
|
|
1561
|
+
strokeWidth=2 doesn't match the overlap-test selectors. */}
|
|
1562
|
+
{clickRipple && (
|
|
1563
|
+
<circle
|
|
1564
|
+
key={clickRipple.ts}
|
|
1565
|
+
cx={clickRipple.x}
|
|
1566
|
+
cy={clickRipple.y}
|
|
1567
|
+
r={clickRipple.r0 + 4}
|
|
1568
|
+
fill="none"
|
|
1569
|
+
stroke={clickRipple.color}
|
|
1570
|
+
strokeWidth="2"
|
|
1571
|
+
opacity="0"
|
|
1572
|
+
style={{ pointerEvents: 'none' }}
|
|
1573
|
+
>
|
|
1574
|
+
<animate attributeName="r" values={`${clickRipple.r0 + 4};${clickRipple.r0 + 30}`} dur="0.5s" fill="freeze" />
|
|
1575
|
+
<animate attributeName="opacity" values="0.7;0" dur="0.5s" fill="freeze" />
|
|
1576
|
+
</circle>
|
|
1577
|
+
)}
|
|
1578
|
+
|
|
1579
|
+
</g>
|
|
1580
|
+
|
|
1581
|
+
{/* #112: overlay panels (recent-signal + legend) render OUTSIDE the
|
|
1582
|
+
zoom/pan <g> so they stay fixed while the topology pans/zooms.
|
|
1583
|
+
They're sized + tucked into the top corners so every corner of
|
|
1584
|
+
each panel is >325px from the canvas centre — i.e. fully outside
|
|
1585
|
+
the outermost (offline) ring. No node on any ring can reach the
|
|
1586
|
+
corner triangles, so the panels never overlap a node, in ring
|
|
1587
|
+
OR grid layout (Vincent 4727 zero-overlap criterion). */}
|
|
1033
1588
|
{/* latest flow labels */}
|
|
1034
|
-
<g transform="translate(
|
|
1035
|
-
<rect x="0" y="0" width="
|
|
1036
|
-
<text x="
|
|
1037
|
-
<text x="
|
|
1038
|
-
{flowLinks.slice(0,
|
|
1039
|
-
<text key={link.key} x="
|
|
1040
|
-
{truncate(link.from,
|
|
1589
|
+
<g transform="translate(16, 16)">
|
|
1590
|
+
<rect x="0" y="0" width="230" height="84" rx="10" fill={pal.legendBox.fill} stroke={pal.legendBox.stroke} opacity={isLight ? 0.97 : 0.92} />
|
|
1591
|
+
<text x="13" y="21" fill={pal.legendHeadline} fontSize="12" fontFamily="monospace" fontWeight="700">recent signal</text>
|
|
1592
|
+
<text x="150" y="21" fill={pal.legendAccent} fontSize="10" fontFamily="monospace">{messages.length} msgs</text>
|
|
1593
|
+
{flowLinks.slice(0, 3).map((link, index) => (
|
|
1594
|
+
<text key={link.key} x="13" y={38 + index * 16} fill={pal.legendText} fontSize="9" fontFamily="monospace">
|
|
1595
|
+
{truncate(link.from, 6)} {'->'} {truncate(link.to, 6)} / {link.count} / {truncate(link.content, 12)}
|
|
1041
1596
|
</text>
|
|
1042
1597
|
))}
|
|
1043
1598
|
</g>
|
|
1044
1599
|
|
|
1045
1600
|
{/* legend */}
|
|
1046
|
-
<g transform="translate(
|
|
1047
|
-
<rect x="0" y="0" width="
|
|
1048
|
-
<circle cx="
|
|
1049
|
-
<text x="
|
|
1050
|
-
<circle cx="
|
|
1051
|
-
<text x="
|
|
1052
|
-
<circle cx="
|
|
1053
|
-
<text x="
|
|
1054
|
-
<path d="
|
|
1055
|
-
</g>
|
|
1601
|
+
<g transform="translate(760, 16)">
|
|
1602
|
+
<rect x="0" y="0" width="224" height="96" rx="10" fill={pal.legendBox.fill} stroke={pal.legendBox.stroke} opacity={isLight ? 0.97 : 0.92} />
|
|
1603
|
+
<circle cx="16" cy="24" r="5.5" fill={isLight ? '#059669' : '#22c55e'} />
|
|
1604
|
+
<text x="30" y="28" fill={pal.legendText} fontSize="11" fontFamily="monospace">working node</text>
|
|
1605
|
+
<circle cx="16" cy="48" r="5.5" fill={isLight ? '#0d9488' : '#2dd4bf'} />
|
|
1606
|
+
<text x="30" y="52" fill={pal.legendText} fontSize="11" fontFamily="monospace">online idle</text>
|
|
1607
|
+
<circle cx="16" cy="72" r="5.5" fill={isLight ? '#94a3b8' : '#6b7280'} />
|
|
1608
|
+
<text x="30" y="76" fill={pal.legendText} fontSize="11" fontFamily="monospace">offline / no SSE</text>
|
|
1609
|
+
<path d="M140,72 Q164,48 196,72" fill="none" stroke={pal.flowEdge} strokeWidth="3" markerEnd="url(#topo-arrow)" />
|
|
1056
1610
|
</g>
|
|
1057
1611
|
</svg>
|
|
1058
1612
|
|
|
@@ -1063,6 +1617,26 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
1063
1617
|
Split into a plain % readout + an explicit reset button with
|
|
1064
1618
|
its own icon + tooltip. */}
|
|
1065
1619
|
<div className="absolute bottom-3 right-3 flex items-center gap-1.5 text-xs select-none">
|
|
1620
|
+
{/* #113: node size — S / M / L segmented control (Vincent 4727). */}
|
|
1621
|
+
<div
|
|
1622
|
+
className="flex items-center rounded-md border overflow-hidden"
|
|
1623
|
+
style={{ background: pal.legendBox.fill, borderColor: pal.containerBorder }}
|
|
1624
|
+
role="group"
|
|
1625
|
+
aria-label="Node size"
|
|
1626
|
+
>
|
|
1627
|
+
{([['S', 0.7], ['M', 0.84], ['L', 1]] as const).map(([lbl, v], idx) => (
|
|
1628
|
+
<button
|
|
1629
|
+
key={lbl}
|
|
1630
|
+
onClick={() => pickNodeScale(v)}
|
|
1631
|
+
aria-pressed={nodeScale === v}
|
|
1632
|
+
title={`Node size: ${lbl === 'S' ? 'small' : lbl === 'M' ? 'medium' : 'large'}`}
|
|
1633
|
+
className={`px-2 py-1 transition-colors ${idx > 0 ? 'border-l' : ''} ${nodeScale === v ? 'bg-cyan-500/15 text-cyan-300' : 'hover:bg-white/5'}`}
|
|
1634
|
+
style={{ color: nodeScale === v ? undefined : pal.legendText, borderColor: pal.containerBorder }}
|
|
1635
|
+
>
|
|
1636
|
+
{lbl}
|
|
1637
|
+
</button>
|
|
1638
|
+
))}
|
|
1639
|
+
</div>
|
|
1066
1640
|
<div
|
|
1067
1641
|
className="flex items-center rounded-md border overflow-hidden"
|
|
1068
1642
|
style={{ background: pal.legendBox.fill, borderColor: pal.containerBorder }}
|
|
@@ -1072,6 +1646,7 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
1072
1646
|
className="px-2 py-1 hover:bg-white/5 transition-colors"
|
|
1073
1647
|
style={{ color: pal.legendText }}
|
|
1074
1648
|
aria-label="Zoom out"
|
|
1649
|
+
title="Zoom out (−)"
|
|
1075
1650
|
>
|
|
1076
1651
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" aria-hidden><path d="M5 12h14" /></svg>
|
|
1077
1652
|
</button>
|
|
@@ -1087,6 +1662,7 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
1087
1662
|
className="px-2 py-1 hover:bg-white/5 transition-colors"
|
|
1088
1663
|
style={{ color: pal.legendText }}
|
|
1089
1664
|
aria-label="Zoom in"
|
|
1665
|
+
title="Zoom in (+)"
|
|
1090
1666
|
>
|
|
1091
1667
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" aria-hidden><path d="M12 5v14M5 12h14" /></svg>
|
|
1092
1668
|
</button>
|
|
@@ -1096,7 +1672,7 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
|
|
|
1096
1672
|
className="p-1.5 rounded-md border hover:bg-white/5 transition-colors"
|
|
1097
1673
|
style={{ background: pal.legendBox.fill, borderColor: pal.containerBorder, color: pal.legendText }}
|
|
1098
1674
|
aria-label="Reset view"
|
|
1099
|
-
title="Reset zoom + pan (or double-click the canvas)"
|
|
1675
|
+
title="Reset zoom + pan (0, or double-click the canvas)"
|
|
1100
1676
|
>
|
|
1101
1677
|
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden>
|
|
1102
1678
|
<path d="M3 12a9 9 0 1 0 9-9 9 9 0 0 0-6.4 2.6L3 8" />
|