@sleep2agi/agent-network-dashboard 0.4.6-preview.15 → 0.4.6-preview.151
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-manifest.json +3 -3
- package/.next/diagnostics/route-bundle-stats.json +41 -41
- package/.next/fallback-build-manifest.json +3 -3
- package/.next/prerender-manifest.json +3 -3
- package/.next/routes-manifest.json +6 -0
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page_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_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/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/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_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_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_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_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_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_client-reference-manifest.js +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_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_client-reference-manifest.js +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_client-reference-manifest.js +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/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_servers_route_actions_08-m-cs.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0tx8s8i._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0tx8s8i._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +4 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_057q.ne._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_057q.ne._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js.map +1 -1
- package/.next/server/middleware-build-manifest.js +3 -3
- 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-mpa_947ipeq.js +1 -0
- package/.next/static/chunks/01lmbsd37fybu.js +1 -0
- package/.next/static/chunks/03o.h6kvmw4l_.js +1 -0
- package/.next/static/chunks/03~~oirxz7~vc.js +1 -0
- package/.next/static/chunks/05b69-7w4fvbn.css +2 -0
- package/.next/static/chunks/05uk96gc~9mni.js +1 -0
- package/.next/static/chunks/08kf-.bgs10ze.js +1 -0
- package/.next/static/chunks/0ax6wj36e_knr.js +7 -0
- package/.next/static/chunks/0d45f.svln5hm.js +1 -0
- package/.next/static/chunks/0f6m4wjhlakrw.js +1 -0
- package/.next/static/chunks/0g1ap~wkozy~6.js +4 -0
- package/.next/static/chunks/0glmrdj9112tc.js +7 -0
- package/.next/static/chunks/0rxrez_cjn.eh.js +1 -0
- package/.next/static/chunks/0x8x.29hkju5a.js +1 -0
- package/.next/static/chunks/174re5ar~kxne.js +1 -0
- 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/ChatPopover.tsx +38 -2
- package/app/components/CommandPalette.tsx +107 -0
- package/app/components/HelpOverlay.tsx +115 -0
- package/app/components/ServersDrawer.tsx +64 -27
- package/app/components/TopoGraph.tsx +2866 -190
- package/app/components/utils.ts +8 -7
- package/app/globals.css +27 -1
- package/app/lib/time.ts +36 -0
- package/package.json +1 -1
- package/scripts/help-overlay-topo-test.mjs +44 -0
- package/scripts/servers-drawer-live-test.mjs +96 -0
- package/scripts/topo-active-filter-pill-test.mjs +101 -0
- package/scripts/topo-active-links-chip-hover-test.mjs +103 -0
- package/scripts/topo-active-links-chip-nav-test.mjs +117 -0
- package/scripts/topo-active-links-chip-test.mjs +73 -0
- package/scripts/topo-active-links-tooltip-test.mjs +79 -0
- package/scripts/topo-arrival-ping-test.mjs +87 -0
- package/scripts/topo-arrow-tier-test.mjs +61 -0
- package/scripts/topo-auto-fit-test.mjs +73 -0
- package/scripts/topo-chat-focus-ring-test.mjs +77 -0
- package/scripts/topo-chat-target-breath-test.mjs +84 -0
- package/scripts/topo-chip-pin-mirror-test.mjs +113 -0
- package/scripts/topo-click-ripple-test.mjs +66 -0
- package/scripts/topo-cmdk-edge-clear-test.mjs +109 -0
- package/scripts/topo-cmdk-layout-view-test.mjs +108 -0
- package/scripts/topo-cmdk-pin-test.mjs +123 -0
- package/scripts/topo-cmdk-vendor-clear-test.mjs +133 -0
- package/scripts/topo-cmdk-vendor-pin-test.mjs +103 -0
- package/scripts/topo-cwd-tooltip-test.mjs +64 -0
- package/scripts/topo-dblclick-reset-test.mjs +74 -0
- package/scripts/topo-dispatch-pulse-test.mjs +83 -0
- package/scripts/topo-edge-badge-click-pin-test.mjs +94 -0
- package/scripts/topo-edge-badge-hot-test.mjs +115 -0
- package/scripts/topo-edge-badge-hover-test.mjs +85 -0
- package/scripts/topo-edge-count-badge-test.mjs +86 -0
- package/scripts/topo-edge-endpoint-highlight-test.mjs +111 -0
- package/scripts/topo-edge-focus-test.mjs +113 -0
- package/scripts/topo-edge-hover-test.mjs +93 -0
- package/scripts/topo-edge-pill-test.mjs +105 -0
- package/scripts/topo-edge-tooltip-test.mjs +70 -0
- package/scripts/topo-endpoint-ring-test.mjs +93 -0
- package/scripts/topo-esc-clears-pin-test.mjs +98 -0
- package/scripts/topo-filter-pill-aliases-test.mjs +121 -0
- package/scripts/topo-filter-pill-body-click-test.mjs +112 -0
- package/scripts/topo-filter-pill-count-test.mjs +110 -0
- package/scripts/topo-filter-pill-mobile-test.mjs +90 -0
- package/scripts/topo-fit-key-test.mjs +106 -0
- package/scripts/topo-freshness-chip-test.mjs +74 -0
- package/scripts/topo-group-box-pinned-state-test.mjs +95 -0
- package/scripts/topo-group-count-test.mjs +68 -0
- package/scripts/topo-group-hover-test.mjs +82 -0
- package/scripts/topo-group-label-hover-test.mjs +119 -0
- package/scripts/topo-group-label-tint-test.mjs +99 -0
- package/scripts/topo-group-label-title-test.mjs +83 -0
- package/scripts/topo-group-pin-test.mjs +119 -0
- package/scripts/topo-group-status-pips-test.mjs +103 -0
- package/scripts/topo-groupbox-hover-lift-test.mjs +127 -0
- package/scripts/topo-groupbox-live-test.mjs +100 -0
- package/scripts/topo-groupbox-march-busyness-test.mjs +155 -0
- package/scripts/topo-groupbox-test.mjs +4 -2
- package/scripts/topo-help-overlay-r109-test.mjs +67 -0
- package/scripts/topo-help-overlay-r118-test.mjs +69 -0
- package/scripts/topo-help-overlay-r123-test.mjs +69 -0
- package/scripts/topo-help-overlay-r134-test.mjs +74 -0
- package/scripts/topo-help-overlay-r137-test.mjs +81 -0
- package/scripts/topo-help-overlay-r141-test.mjs +86 -0
- package/scripts/topo-help-overlay-r59-test.mjs +67 -0
- package/scripts/topo-help-overlay-r65-test.mjs +64 -0
- package/scripts/topo-help-overlay-r70-test.mjs +63 -0
- package/scripts/topo-help-overlay-r78-test.mjs +59 -0
- package/scripts/topo-help-overlay-r81-test.mjs +58 -0
- package/scripts/topo-help-overlay-r87-test.mjs +64 -0
- package/scripts/topo-help-overlay-r91-test.mjs +65 -0
- package/scripts/topo-hub-busyness-test.mjs +100 -0
- package/scripts/topo-hub-click-fit-test.mjs +137 -0
- package/scripts/topo-hub-hover-ring-test.mjs +82 -0
- package/scripts/topo-hub-title-test.mjs +93 -0
- package/scripts/topo-hub-working-count-test.mjs +99 -0
- package/scripts/topo-ingroup-edge-test.mjs +103 -0
- package/scripts/topo-label-lift-test.mjs +119 -0
- package/scripts/topo-label-scale-test.mjs +78 -0
- package/scripts/topo-lastseen-tooltip-test.mjs +59 -0
- package/scripts/topo-layout-key-test.mjs +91 -0
- package/scripts/topo-legend-count-test.mjs +88 -0
- package/scripts/topo-legend-hover-test.mjs +113 -0
- package/scripts/topo-legend-panel-header-test.mjs +83 -0
- package/scripts/topo-legend-pin-test.mjs +128 -0
- package/scripts/topo-legend-row-hover-lift-test.mjs +109 -0
- package/scripts/topo-legend-row-tint-test.mjs +95 -0
- package/scripts/topo-minimap-test.mjs +68 -0
- package/scripts/topo-mobile-chips-test.mjs +103 -0
- package/scripts/topo-node-hover-lift-test.mjs +85 -0
- package/scripts/topo-node-title-test.mjs +101 -0
- package/scripts/topo-offline-dim-test.mjs +70 -0
- package/scripts/topo-online-chip-nav-test.mjs +143 -0
- package/scripts/topo-orbit-busyness-test.mjs +96 -0
- package/scripts/topo-overlap-test.mjs +32 -5
- package/scripts/topo-pan-cursor-test.mjs +62 -0
- package/scripts/topo-panel-elevation-test.mjs +76 -0
- package/scripts/topo-panel-hover-elevation-test.mjs +118 -0
- package/scripts/topo-particle-stagger-test.mjs +82 -0
- package/scripts/topo-pin-intersection-empty-test.mjs +129 -0
- package/scripts/topo-pin-intersection-test.mjs +121 -0
- package/scripts/topo-pin-persistence-test.mjs +105 -0
- package/scripts/topo-popover-meta-test.mjs +78 -0
- package/scripts/topo-pressure-bar-test.mjs +69 -0
- package/scripts/topo-pressure-hover-test.mjs +123 -0
- package/scripts/topo-pressure-pin-test.mjs +119 -0
- package/scripts/topo-pressure-seg-tooltip-test.mjs +98 -0
- package/scripts/topo-pulse-rate-test.mjs +71 -0
- package/scripts/topo-recent-panel-count-test.mjs +72 -0
- package/scripts/topo-recent-panel-hot-header-test.mjs +132 -0
- package/scripts/topo-recent-panel-more-nav-test.mjs +127 -0
- package/scripts/topo-recent-panel-more-test.mjs +106 -0
- package/scripts/topo-recent-row-hot-test.mjs +110 -0
- package/scripts/topo-recent-row-hover-lift-test.mjs +128 -0
- package/scripts/topo-recent-row-hover-test.mjs +122 -0
- package/scripts/topo-recent-row-pin-test.mjs +126 -0
- package/scripts/topo-recent-row-tint-test.mjs +91 -0
- package/scripts/topo-recent-row-ts-test.mjs +95 -0
- package/scripts/topo-recent-row-typography-test.mjs +119 -0
- package/scripts/topo-recent-signal-empty-hint-test.mjs +73 -0
- package/scripts/topo-recent-signal-empty-test.mjs +63 -0
- package/scripts/topo-reduced-motion-test.mjs +77 -0
- package/scripts/topo-status-chip-hover-test.mjs +101 -0
- package/scripts/topo-status-chip-tooltip-test.mjs +95 -0
- package/scripts/topo-tier-ring-occupancy-test.mjs +87 -0
- package/scripts/topo-tier-ring-test.mjs +69 -0
- package/scripts/topo-tier-ring-tint-test.mjs +110 -0
- package/scripts/topo-tier-stagger-test.mjs +89 -0
- package/scripts/topo-tz-ghost-test.mjs +69 -0
- package/scripts/topo-vendor-chip-hover-test.mjs +111 -0
- package/scripts/topo-vendor-letter-tooltip-test.mjs +92 -0
- package/scripts/topo-vendor-pill-test.mjs +125 -0
- package/scripts/topo-vendor-pin-test.mjs +125 -0
- package/scripts/topo-wheel-zoom-test.mjs +79 -0
- package/scripts/topo-working-chip-pin-test.mjs +148 -0
- package/scripts/topo-working-halo-breath-test.mjs +86 -0
- package/scripts/topo-zoom-keys-test.mjs +89 -0
- package/.next/static/chunks/0-7iowzu4.798.js +0 -1
- package/.next/static/chunks/02t5jr4shh.hy.js +0 -1
- package/.next/static/chunks/0_qfg5s5uzs7..js +0 -7
- package/.next/static/chunks/0e8tvxp4z.y4s.js +0 -1
- package/.next/static/chunks/0elalb9n_.~di.js +0 -1
- package/.next/static/chunks/0fqy_0wj_1xa7.js +0 -7
- package/.next/static/chunks/0j_q__o8p6g83.js +0 -1
- package/.next/static/chunks/0jl6_w50zh4l5.js +0 -1
- package/.next/static/chunks/0nia.a1mfg7q1.js +0 -1
- package/.next/static/chunks/0r4q73.-1bkyl.js +0 -1
- package/.next/static/chunks/0r_c8eccg3z2o.js +0 -1
- package/.next/static/chunks/10oi7l-tycgl9.js +0 -1
- package/.next/static/chunks/11uk1izuv3~0t.js +0 -1
- package/.next/static/chunks/13vw-ubg-9pjp.css +0 -2
- package/.next/static/chunks/14m.l5k.wx7cx.js +0 -1
- /package/.next/static/{-hO5QrBL0EPsoBbzcd662 → jNoJAOY9PpVhTslXxUvqQ}/_buildManifest.js +0 -0
- /package/.next/static/{-hO5QrBL0EPsoBbzcd662 → jNoJAOY9PpVhTslXxUvqQ}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{-hO5QrBL0EPsoBbzcd662 → jNoJAOY9PpVhTslXxUvqQ}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/* Round 51 verification: hovered node <g> lifts -2px via CSS transform.
|
|
2
|
+
* - At rest, every node's CSS transform is "none" (or matrix with 0 ty).
|
|
3
|
+
* - On hover, the hovered <g> reports `translateY(-2px)` (matrix(1,0,0,1,0,-2))
|
|
4
|
+
* via getComputedStyle.transform, while siblings stay at identity.
|
|
5
|
+
* - Release: returns to identity.
|
|
6
|
+
* - With prefers-reduced-motion forced, NO lift even on hover.
|
|
7
|
+
*/
|
|
8
|
+
import { chromium } from 'playwright';
|
|
9
|
+
import { readFileSync } from 'node:fs';
|
|
10
|
+
|
|
11
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
12
|
+
const browser = await chromium.launch({ headless: true });
|
|
13
|
+
|
|
14
|
+
async function probe(reducedMotion) {
|
|
15
|
+
const ctx = await browser.newContext({
|
|
16
|
+
viewport: { width: 1280, height: 900 },
|
|
17
|
+
reducedMotion: reducedMotion ? 'reduce' : 'no-preference',
|
|
18
|
+
});
|
|
19
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
20
|
+
await ctx.addInitScript(() => {
|
|
21
|
+
try {
|
|
22
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
23
|
+
localStorage.setItem('anet-topo-layout', 'ring');
|
|
24
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
25
|
+
} catch {}
|
|
26
|
+
});
|
|
27
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
28
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
29
|
+
const r = await route.fetch();
|
|
30
|
+
const b = await r.json();
|
|
31
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
32
|
+
const sessions = ['alpha', 'beta', 'gamma'].map(a => ({
|
|
33
|
+
alias: a, status: 'idle', network_id: nid, project_dir: null,
|
|
34
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
35
|
+
}));
|
|
36
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
37
|
+
});
|
|
38
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
39
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
40
|
+
const page = await ctx.newPage();
|
|
41
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
42
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
|
|
43
|
+
await page.waitForTimeout(500);
|
|
44
|
+
|
|
45
|
+
const readTy = (alias) => page.evaluate(a => {
|
|
46
|
+
const el = document.querySelector(`g[data-node="${a}"]`);
|
|
47
|
+
if (!el) return null;
|
|
48
|
+
const m = getComputedStyle(el).transform;
|
|
49
|
+
if (!m || m === 'none') return 0;
|
|
50
|
+
// matrix(a, b, c, d, tx, ty) — extract ty
|
|
51
|
+
const parts = m.match(/matrix\(([^)]+)\)/);
|
|
52
|
+
if (!parts) return null;
|
|
53
|
+
const arr = parts[1].split(',').map(s => parseFloat(s.trim()));
|
|
54
|
+
return arr[5] ?? null;
|
|
55
|
+
}, alias);
|
|
56
|
+
|
|
57
|
+
const restAlpha = await readTy('alpha');
|
|
58
|
+
const restBeta = await readTy('beta');
|
|
59
|
+
await page.locator('g[data-node="alpha"]').hover({ force: true });
|
|
60
|
+
await page.waitForTimeout(300);
|
|
61
|
+
const hoveredAlpha = await readTy('alpha');
|
|
62
|
+
const hoveredBeta = await readTy('beta');
|
|
63
|
+
await page.mouse.move(10, 10);
|
|
64
|
+
await page.waitForTimeout(300);
|
|
65
|
+
const releasedAlpha = await readTy('alpha');
|
|
66
|
+
|
|
67
|
+
await ctx.close();
|
|
68
|
+
return { restAlpha, restBeta, hoveredAlpha, hoveredBeta, releasedAlpha };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const normal = await probe(false);
|
|
72
|
+
const reduced = await probe(true);
|
|
73
|
+
await browser.close();
|
|
74
|
+
|
|
75
|
+
const results = {
|
|
76
|
+
normal_restAtZero: normal.restAlpha === 0 && normal.restBeta === 0,
|
|
77
|
+
normal_hoveredLifts: normal.hoveredAlpha === -2,
|
|
78
|
+
normal_siblingStays: normal.hoveredBeta === 0,
|
|
79
|
+
normal_released: normal.releasedAlpha === 0,
|
|
80
|
+
reduced_noLift: reduced.hoveredAlpha === 0,
|
|
81
|
+
};
|
|
82
|
+
const ok = Object.values(results).every(Boolean);
|
|
83
|
+
console.log(`${ok ? '✅' : '❌'} node hover lift:`, JSON.stringify(results),
|
|
84
|
+
`\n normal=`, normal, `\n reduced=`, reduced);
|
|
85
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/* Round 98 verification: node <title> tooltips now include status,
|
|
2
|
+
* group membership (when applicable), and per-node flow counts
|
|
3
|
+
* (inbound/outbound) alongside the existing identity/cwd/last-seen
|
|
4
|
+
* lines. Same info-density spirit as R97 pill tooltips — hover any
|
|
5
|
+
* node and the answer is right there.
|
|
6
|
+
*
|
|
7
|
+
* Fleet:
|
|
8
|
+
* - alpha1 (working, claude) + alpha2 (idle, claude) → alpha group of 2
|
|
9
|
+
* - solo (working, gpt-5) — no group (singleton)
|
|
10
|
+
* - off1 (offline, claude) — has last_seen_at older
|
|
11
|
+
*
|
|
12
|
+
* Flows:
|
|
13
|
+
* alpha1 → solo × 3
|
|
14
|
+
* solo → alpha1 × 1
|
|
15
|
+
* ⇒ alpha1: 1 in / 3 out; solo: 3 in / 1 out; alpha2/off1: no flows
|
|
16
|
+
*/
|
|
17
|
+
import { chromium } from 'playwright';
|
|
18
|
+
import { readFileSync } from 'node:fs';
|
|
19
|
+
|
|
20
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
21
|
+
const browser = await chromium.launch({ headless: true });
|
|
22
|
+
|
|
23
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
|
|
24
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
25
|
+
await ctx.addInitScript(() => {
|
|
26
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
30
|
+
const stale = new Date(Date.now() - 20 * 60 * 1000).toISOString();
|
|
31
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
32
|
+
const r = await route.fetch();
|
|
33
|
+
const b = await r.json();
|
|
34
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
35
|
+
const mk = (alias, status, model, ts) => ({
|
|
36
|
+
alias, status, model, runtime: 'cli-claude-code',
|
|
37
|
+
network_id: nid, project_dir: null,
|
|
38
|
+
created_at: ts, updated_at: ts, last_seen_at: ts,
|
|
39
|
+
});
|
|
40
|
+
const sessions = [
|
|
41
|
+
mk('alpha1', 'working', 'claude-opus-4', fresh),
|
|
42
|
+
mk('alpha2', 'idle', 'claude-opus-4', fresh),
|
|
43
|
+
mk('solo', 'working', 'gpt-5', fresh),
|
|
44
|
+
mk('off1', 'offline', 'claude-opus-4', stale),
|
|
45
|
+
];
|
|
46
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const now = Date.now();
|
|
50
|
+
const mkMsg = (id, from_alias, to_alias, ageMs) => ({
|
|
51
|
+
id, from_alias, to_alias, content: 'hi', network_id: 'default',
|
|
52
|
+
created_at: new Date(now - ageMs).toISOString(),
|
|
53
|
+
});
|
|
54
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
|
|
55
|
+
mkMsg('m1', 'alpha1', 'solo', 30 * 1000),
|
|
56
|
+
mkMsg('m2', 'alpha1', 'solo', 60 * 1000),
|
|
57
|
+
mkMsg('m3', 'alpha1', 'solo', 90 * 1000),
|
|
58
|
+
mkMsg('m4', 'solo', 'alpha1', 45 * 1000),
|
|
59
|
+
] } }));
|
|
60
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
61
|
+
const page = await ctx.newPage();
|
|
62
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
63
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
64
|
+
await page.waitForTimeout(400);
|
|
65
|
+
|
|
66
|
+
const titles = await page.evaluate(() => {
|
|
67
|
+
const out = {};
|
|
68
|
+
for (const alias of ['alpha1', 'alpha2', 'solo', 'off1']) {
|
|
69
|
+
const g = document.querySelector(`g[data-node="${alias}"]`);
|
|
70
|
+
out[alias] = g?.querySelector('title')?.textContent || '';
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
await browser.close();
|
|
76
|
+
|
|
77
|
+
const results = {
|
|
78
|
+
// Status line is the heading.
|
|
79
|
+
alpha1_statusHeading: /^alpha1 · working/m.test(titles.alpha1),
|
|
80
|
+
alpha2_statusHeading: /^alpha2 · idle/m.test(titles.alpha2),
|
|
81
|
+
solo_statusHeading: /^solo · working/m.test(titles.solo),
|
|
82
|
+
off1_statusHeading: /^off1 · offline/m.test(titles.off1),
|
|
83
|
+
// Group line only on multi-member band.
|
|
84
|
+
alpha1_groupShown: /group: alpha · 2/.test(titles.alpha1),
|
|
85
|
+
alpha2_groupShown: /group: alpha · 2/.test(titles.alpha2),
|
|
86
|
+
solo_groupSkipped: !/group:/.test(titles.solo),
|
|
87
|
+
// Flow counts on participating nodes only.
|
|
88
|
+
alpha1_flowsCorrect: /flows: 1 in \/ 3 out/.test(titles.alpha1),
|
|
89
|
+
solo_flowsCorrect: /flows: 3 in \/ 1 out/.test(titles.solo),
|
|
90
|
+
alpha2_noFlows: !/flows:/.test(titles.alpha2),
|
|
91
|
+
off1_lastSeen: /last seen:/.test(titles.off1),
|
|
92
|
+
// Identity line still present.
|
|
93
|
+
alpha1_hasIdentity: /claude/i.test(titles.alpha1),
|
|
94
|
+
};
|
|
95
|
+
const ok = Object.values(results).every(Boolean);
|
|
96
|
+
console.log(`${ok ? '✅' : '❌'} node titles:`, JSON.stringify(results),
|
|
97
|
+
`\n alpha1:\n${titles.alpha1}`,
|
|
98
|
+
`\n alpha2:\n${titles.alpha2}`,
|
|
99
|
+
`\n solo:\n${titles.solo}`,
|
|
100
|
+
`\n off1:\n${titles.off1}`);
|
|
101
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/* Round 17 verification: offline nodes render at opacity 0.6 at rest,
|
|
2
|
+
* online stay at 1, and the chat-focused node (even if offline) jumps
|
|
3
|
+
* to 1 when its ChatPopover opens. */
|
|
4
|
+
import { chromium } from 'playwright';
|
|
5
|
+
import { readFileSync } from 'node:fs';
|
|
6
|
+
|
|
7
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
8
|
+
const browser = await chromium.launch({ headless: true });
|
|
9
|
+
|
|
10
|
+
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
|
11
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
12
|
+
await ctx.addInitScript(() => {
|
|
13
|
+
try {
|
|
14
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
15
|
+
localStorage.removeItem('anet-brand');
|
|
16
|
+
localStorage.removeItem('anet-topo-view');
|
|
17
|
+
localStorage.setItem('anet-topo-layout', 'grid');
|
|
18
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
19
|
+
} catch {}
|
|
20
|
+
});
|
|
21
|
+
// fresh-enough so the ghost-age-out filter doesn't drop the offline node
|
|
22
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
23
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
24
|
+
const r = await route.fetch();
|
|
25
|
+
const b = await r.json();
|
|
26
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
27
|
+
const sessions = [
|
|
28
|
+
{ alias: 'on1', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
29
|
+
{ alias: 'on2', status: 'working', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
30
|
+
{ alias: 'off1', status: 'offline', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
31
|
+
{ alias: 'off2', status: 'offline', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
32
|
+
];
|
|
33
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
34
|
+
});
|
|
35
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
36
|
+
const page = await ctx.newPage();
|
|
37
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
38
|
+
await page.waitForFunction(() => {
|
|
39
|
+
const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
|
|
40
|
+
return !!svg && svg.querySelectorAll('g[data-node]').length === 4;
|
|
41
|
+
}, { timeout: 30000 }).catch(() => {});
|
|
42
|
+
await page.waitForTimeout(500);
|
|
43
|
+
|
|
44
|
+
const opacityOf = (alias) => page.$eval(`g[data-node="${alias}"]`, el =>
|
|
45
|
+
parseFloat(getComputedStyle(el).opacity || el.style.opacity || '1'));
|
|
46
|
+
|
|
47
|
+
const restOn1 = await opacityOf('on1');
|
|
48
|
+
const restOn2 = await opacityOf('on2');
|
|
49
|
+
const restOff1 = await opacityOf('off1');
|
|
50
|
+
const restOff2 = await opacityOf('off2');
|
|
51
|
+
|
|
52
|
+
// Click an offline node — the chat popover should open and that node
|
|
53
|
+
// should jump to full opacity.
|
|
54
|
+
await page.locator('g[data-node="off1"]').click();
|
|
55
|
+
await page.waitForTimeout(300);
|
|
56
|
+
const focusedOff1 = await opacityOf('off1');
|
|
57
|
+
const otherOff2 = await opacityOf('off2');
|
|
58
|
+
|
|
59
|
+
await browser.close();
|
|
60
|
+
const results = {
|
|
61
|
+
onlineFullOpacity: Math.abs(restOn1 - 1) < 0.01 && Math.abs(restOn2 - 1) < 0.01,
|
|
62
|
+
offlineDimmedAtRest: Math.abs(restOff1 - 0.6) < 0.01 && Math.abs(restOff2 - 0.6) < 0.01,
|
|
63
|
+
chatFocusOfflineRestoresOpacity: Math.abs(focusedOff1 - 1) < 0.01,
|
|
64
|
+
otherOfflineStaysDimmed: Math.abs(otherOff2 - 0.6) < 0.01,
|
|
65
|
+
};
|
|
66
|
+
const ok = Object.values(results).every(Boolean);
|
|
67
|
+
console.log(`${ok ? '✅' : '❌'} offline dim:`, JSON.stringify(results),
|
|
68
|
+
`rest=on1:${restOn1} on2:${restOn2} off1:${restOff1} off2:${restOff2}`,
|
|
69
|
+
`clicked off1=${focusedOff1} off2=${otherOff2}`);
|
|
70
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/* Round 140 verification: the "N online" chip in the chip row
|
|
2
|
+
* gets an onClick that navigates to /nodes when interactive.
|
|
3
|
+
* Mirrors R136 active-links→/messages idiom; complements R139
|
|
4
|
+
* working→pin idiom. The online chip is the third chip-row chip
|
|
5
|
+
* that had cursor:pointer without a click action since R79.
|
|
6
|
+
*
|
|
7
|
+
* Why nav not pin: "online" = working + idle in semantics; there's
|
|
8
|
+
* no single pinnedStatus value that captures both. /nodes is the
|
|
9
|
+
* natural full-list destination.
|
|
10
|
+
*
|
|
11
|
+
* Three states tested:
|
|
12
|
+
* - Empty fleet (onlineCount=0): chip not interactive
|
|
13
|
+
* - 1 online: clickable, role=link, click → /nodes
|
|
14
|
+
* - 5 online (mix of working+idle): same shape, click → /nodes
|
|
15
|
+
*
|
|
16
|
+
* Hover preview still works (R79 highlight all online) — that's
|
|
17
|
+
* a separate gesture verified in topo-active-links-chip-hover-test
|
|
18
|
+
* for the chip-row's hover ladder.
|
|
19
|
+
*/
|
|
20
|
+
import { chromium } from 'playwright';
|
|
21
|
+
import { readFileSync } from 'node:fs';
|
|
22
|
+
|
|
23
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
24
|
+
|
|
25
|
+
// status: 'working' | 'idle' | 'offline' — distribute by counts
|
|
26
|
+
async function probe({ working, idle, offline }) {
|
|
27
|
+
const browser = await chromium.launch({ headless: true });
|
|
28
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
|
|
29
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
30
|
+
await ctx.addInitScript(() => {
|
|
31
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
32
|
+
});
|
|
33
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
34
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
35
|
+
const r = await route.fetch();
|
|
36
|
+
const b = await r.json();
|
|
37
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
38
|
+
const sessions = [];
|
|
39
|
+
let i = 0;
|
|
40
|
+
for (let n = 0; n < working; n++, i++) sessions.push(mk(i, 'working', fresh, nid));
|
|
41
|
+
for (let n = 0; n < idle; n++, i++) sessions.push(mk(i, 'idle', fresh, nid));
|
|
42
|
+
for (let n = 0; n < offline; n++, i++) sessions.push(mk(i, 'offline', fresh, nid));
|
|
43
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
44
|
+
});
|
|
45
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
46
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
47
|
+
|
|
48
|
+
const page = await ctx.newPage();
|
|
49
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
50
|
+
// offline-only fleet won't render g[data-node] (only online ones do),
|
|
51
|
+
// but the chip row still renders because sessions.length > 0.
|
|
52
|
+
await page.waitForSelector('[data-online-chip]', { timeout: 10000 });
|
|
53
|
+
await page.waitForTimeout(400);
|
|
54
|
+
|
|
55
|
+
return { browser, page };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function mk(i, status, fresh, nid) {
|
|
59
|
+
return {
|
|
60
|
+
alias: `node${i}`,
|
|
61
|
+
status,
|
|
62
|
+
model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
63
|
+
network_id: nid, project_dir: null,
|
|
64
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// State A: 5 online (3 working / 2 idle)
|
|
69
|
+
const { browser: b1, page: p1 } = await probe({ working: 3, idle: 2, offline: 0 });
|
|
70
|
+
const fiveProbe = await p1.evaluate(() => {
|
|
71
|
+
const chip = document.querySelector('[data-online-chip]');
|
|
72
|
+
return {
|
|
73
|
+
text: chip?.textContent,
|
|
74
|
+
clickable: chip?.getAttribute('data-online-chip-clickable'),
|
|
75
|
+
role: chip?.getAttribute('role'),
|
|
76
|
+
tabindex: chip?.getAttribute('tabindex'),
|
|
77
|
+
cursor: chip?.getAttribute('style')?.includes('cursor: pointer'),
|
|
78
|
+
title: chip?.getAttribute('title'),
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
await p1.locator('[data-online-chip]').click();
|
|
82
|
+
await p1.waitForURL('**/nodes', { timeout: 10000 });
|
|
83
|
+
const fiveUrl = p1.url();
|
|
84
|
+
await b1.close();
|
|
85
|
+
|
|
86
|
+
// State B: 1 online (1 idle, 0 working)
|
|
87
|
+
const { browser: b2, page: p2 } = await probe({ working: 0, idle: 1, offline: 0 });
|
|
88
|
+
const oneProbe = await p2.evaluate(() => {
|
|
89
|
+
const chip = document.querySelector('[data-online-chip]');
|
|
90
|
+
return {
|
|
91
|
+
text: chip?.textContent,
|
|
92
|
+
clickable: chip?.getAttribute('data-online-chip-clickable'),
|
|
93
|
+
role: chip?.getAttribute('role'),
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
await p2.locator('[data-online-chip]').click();
|
|
97
|
+
await p2.waitForURL('**/nodes', { timeout: 10000 });
|
|
98
|
+
const oneUrl = p2.url();
|
|
99
|
+
await b2.close();
|
|
100
|
+
|
|
101
|
+
// State C: offline-only fleet (2 sessions, all offline → onlineNodes=0)
|
|
102
|
+
// 0-session fleet hits Overview's R52/R70 first-run CTA path, not
|
|
103
|
+
// TopoGraph; offline-only keeps the chip row alive so the non-
|
|
104
|
+
// interactive contract on "0 online" can be verified.
|
|
105
|
+
const { browser: b3, page: p3 } = await probe({ working: 0, idle: 0, offline: 2 });
|
|
106
|
+
const emptyProbe = await p3.evaluate(() => {
|
|
107
|
+
const chip = document.querySelector('[data-online-chip]');
|
|
108
|
+
return {
|
|
109
|
+
text: chip?.textContent,
|
|
110
|
+
clickable: chip?.getAttribute('data-online-chip-clickable'),
|
|
111
|
+
role: chip?.getAttribute('role'),
|
|
112
|
+
tabindex: chip?.getAttribute('tabindex'),
|
|
113
|
+
cursor: chip?.getAttribute('style')?.includes('cursor: pointer'),
|
|
114
|
+
};
|
|
115
|
+
});
|
|
116
|
+
await b3.close();
|
|
117
|
+
|
|
118
|
+
const results = {
|
|
119
|
+
five_text5Online: fiveProbe.text === '5 online',
|
|
120
|
+
five_clickableTrue: fiveProbe.clickable === 'true',
|
|
121
|
+
five_roleLink: fiveProbe.role === 'link',
|
|
122
|
+
five_tabIndex0: fiveProbe.tabindex === '0',
|
|
123
|
+
five_cursorPointer: fiveProbe.cursor === true,
|
|
124
|
+
five_titleMentionsNodes: (fiveProbe.title || '').includes('/nodes'),
|
|
125
|
+
five_navToNodes: /\/nodes$/.test(fiveUrl),
|
|
126
|
+
|
|
127
|
+
one_text1Online: oneProbe.text === '1 online',
|
|
128
|
+
one_clickableTrue: oneProbe.clickable === 'true',
|
|
129
|
+
one_roleLink: oneProbe.role === 'link',
|
|
130
|
+
one_navToNodes: /\/nodes$/.test(oneUrl),
|
|
131
|
+
|
|
132
|
+
empty_text0Online: emptyProbe.text === '0 online',
|
|
133
|
+
empty_clickableFalse: emptyProbe.clickable === 'false',
|
|
134
|
+
empty_noRole: emptyProbe.role === null || emptyProbe.role === undefined,
|
|
135
|
+
empty_noTabIndex: emptyProbe.tabindex === null || emptyProbe.tabindex === undefined,
|
|
136
|
+
empty_noCursor: !emptyProbe.cursor,
|
|
137
|
+
};
|
|
138
|
+
const ok = Object.values(results).every(Boolean);
|
|
139
|
+
console.log(`${ok ? '✅' : '❌'} online chip nav:`, JSON.stringify(results),
|
|
140
|
+
`\n fiveProbe=`, fiveProbe, ` fiveUrl=`, fiveUrl,
|
|
141
|
+
`\n oneProbe=`, oneProbe, ` oneUrl=`, oneUrl,
|
|
142
|
+
`\n emptyProbe=`, emptyProbe);
|
|
143
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/* Round 131 verification: outer-ring orbit period buckets on
|
|
2
|
+
* workingCount, mirroring R84's hub-breath cadence. Idle fleet
|
|
3
|
+
* keeps the original 16s "calm sweep"; as work piles up the
|
|
4
|
+
* orbit accelerates to 14 / 12 / 10s. Both motion layers now
|
|
5
|
+
* read the same underlying "is the network busy" signal.
|
|
6
|
+
*
|
|
7
|
+
* Bucket boundaries identical to R84 (line ~2702):
|
|
8
|
+
* workingCount === 0 → bucket 0, dur 16s
|
|
9
|
+
* workingCount in [1, 2] → bucket 1, dur 14s
|
|
10
|
+
* workingCount in [3, 5] → bucket 2, dur 12s
|
|
11
|
+
* workingCount >= 6 → bucket 3, dur 10s
|
|
12
|
+
*
|
|
13
|
+
* R50 only renders on cyber theme (light is skipped to keep the
|
|
14
|
+
* white surface clean). All probes use cyber.
|
|
15
|
+
*
|
|
16
|
+
* Probe fleet sizes pick one representative per bucket.
|
|
17
|
+
*/
|
|
18
|
+
import { chromium } from 'playwright';
|
|
19
|
+
import { readFileSync } from 'node:fs';
|
|
20
|
+
|
|
21
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
22
|
+
|
|
23
|
+
async function probe(workingCount, totalCount) {
|
|
24
|
+
const browser = await chromium.launch({ headless: true });
|
|
25
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
|
|
26
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
27
|
+
await ctx.addInitScript(() => {
|
|
28
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
29
|
+
});
|
|
30
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
31
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
32
|
+
const r = await route.fetch();
|
|
33
|
+
const b = await r.json();
|
|
34
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
35
|
+
const sessions = [];
|
|
36
|
+
for (let i = 0; i < totalCount; i++) {
|
|
37
|
+
sessions.push({
|
|
38
|
+
alias: `node${i}`,
|
|
39
|
+
status: i < workingCount ? 'working' : 'idle',
|
|
40
|
+
model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
41
|
+
network_id: nid, project_dir: null,
|
|
42
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
46
|
+
});
|
|
47
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
48
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
49
|
+
|
|
50
|
+
const page = await ctx.newPage();
|
|
51
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
52
|
+
await page.waitForFunction((n) => document.querySelectorAll('g[data-node]').length === n, totalCount, { timeout: 30000 });
|
|
53
|
+
await page.waitForTimeout(400);
|
|
54
|
+
|
|
55
|
+
const out = await page.evaluate(() => {
|
|
56
|
+
const orbits = [...document.querySelectorAll('[data-topo-orbit-bucket]')];
|
|
57
|
+
return orbits.map(g => ({
|
|
58
|
+
bucket: g.getAttribute('data-topo-orbit-bucket'),
|
|
59
|
+
dur: g.getAttribute('data-topo-orbit-dur'),
|
|
60
|
+
animDur: g.querySelector('animateTransform')?.getAttribute('dur'),
|
|
61
|
+
}));
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
await browser.close();
|
|
65
|
+
return out;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Pick one from each bucket
|
|
69
|
+
const b0 = await probe(0, 4); // workingCount=0 → bucket 0, dur 16
|
|
70
|
+
const b1 = await probe(2, 4); // workingCount=2 → bucket 1, dur 14
|
|
71
|
+
const b2 = await probe(4, 5); // workingCount=4 → bucket 2, dur 12
|
|
72
|
+
const b3 = await probe(7, 8); // workingCount=7 → bucket 3, dur 10
|
|
73
|
+
|
|
74
|
+
const allConsistent = (orbits, bucket, dur) =>
|
|
75
|
+
orbits.length === 4 &&
|
|
76
|
+
orbits.every(o => o.bucket === String(bucket) && o.dur === String(dur) && o.animDur === `${dur}s`);
|
|
77
|
+
|
|
78
|
+
const results = {
|
|
79
|
+
b0_fourOrbits: b0.length === 4,
|
|
80
|
+
b0_bucket0_16s: allConsistent(b0, 0, 16),
|
|
81
|
+
b1_bucket1_14s: allConsistent(b1, 1, 14),
|
|
82
|
+
b2_bucket2_12s: allConsistent(b2, 2, 12),
|
|
83
|
+
b3_bucket3_10s: allConsistent(b3, 3, 10),
|
|
84
|
+
// dur strictly decreases as busy increases
|
|
85
|
+
dur_monotonic:
|
|
86
|
+
Number(b0[0]?.dur) > Number(b1[0]?.dur) &&
|
|
87
|
+
Number(b1[0]?.dur) > Number(b2[0]?.dur) &&
|
|
88
|
+
Number(b2[0]?.dur) > Number(b3[0]?.dur),
|
|
89
|
+
};
|
|
90
|
+
const ok = Object.values(results).every(Boolean);
|
|
91
|
+
console.log(`${ok ? '✅' : '❌'} orbit busyness:`, JSON.stringify(results),
|
|
92
|
+
`\n b0(0w/4)=`, b0,
|
|
93
|
+
`\n b1(2w/4)=`, b1,
|
|
94
|
+
`\n b2(4w/5)=`, b2,
|
|
95
|
+
`\n b3(7w/8)=`, b3);
|
|
96
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
* - node ↔ node — status rings don't intersect
|
|
4
4
|
* - node ↔ panel — no node circle intersects an overlay panel rect
|
|
5
5
|
* - node ↔ group-label — no group-box label text covers a node
|
|
6
|
-
* - group-box ↔ box — no two group boxes overlap
|
|
6
|
+
* - group-box ↔ box — no two group boxes overlap
|
|
7
|
+
* - label ↔ label — no two text labels overlap (Round 27 — caught
|
|
8
|
+
* the dense-node-label vs group-label collision
|
|
9
|
+
* Vincent reported in preview.29) */
|
|
7
10
|
import { chromium } from 'playwright';
|
|
8
11
|
import { readFileSync, mkdirSync } from 'node:fs';
|
|
9
12
|
|
|
@@ -80,11 +83,24 @@ async function check(layout) {
|
|
|
80
83
|
label: { x: bb.x, y: bb.y, w: bb.width, h: bb.height },
|
|
81
84
|
};
|
|
82
85
|
});
|
|
83
|
-
|
|
86
|
+
// Round 27 / P0: every visible label bbox (node alias text + group
|
|
87
|
+
// headings) so we can assert label↔label clearance. Skip elements
|
|
88
|
+
// with empty bboxes (e.g., text inside an unrendered <g>).
|
|
89
|
+
const allLabels = [];
|
|
90
|
+
for (const t of svg.querySelectorAll('g[data-node] text, g[data-group] text')) {
|
|
91
|
+
const bb = t.getBBox();
|
|
92
|
+
if (bb.width === 0 || bb.height === 0) continue;
|
|
93
|
+
allLabels.push({
|
|
94
|
+
kind: t.closest('g[data-group]') ? 'group' : 'node',
|
|
95
|
+
owner: (t.closest('g[data-group]')?.getAttribute('data-group')) || (t.closest('g[data-node]')?.getAttribute('data-node')) || '?',
|
|
96
|
+
x: bb.x, y: bb.y, w: bb.width, h: bb.height,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
return { nodes, panels, groups, allLabels };
|
|
84
100
|
});
|
|
85
101
|
|
|
86
102
|
const TOL = 2; // px slack — touching edges is fine, real overlap is not
|
|
87
|
-
let nodeNode = 0, nodePanel = 0, nodeLabel = 0, boxBox = 0;
|
|
103
|
+
let nodeNode = 0, nodePanel = 0, nodeLabel = 0, boxBox = 0, labelLabel = 0;
|
|
88
104
|
|
|
89
105
|
// node ↔ node
|
|
90
106
|
for (let i = 0; i < data.nodes.length; i++) {
|
|
@@ -119,12 +135,23 @@ async function check(layout) {
|
|
|
119
135
|
overlaps1D(a.y, a.y + a.h, b.y, b.y + b.h, TOL)) boxBox++;
|
|
120
136
|
}
|
|
121
137
|
}
|
|
138
|
+
// label ↔ label (Round 27)
|
|
139
|
+
// Same-node node-labels (e.g. alias + sub-status in the full card) are
|
|
140
|
+
// intentionally close and adjacent — skip pairs that share an owner.
|
|
141
|
+
for (let i = 0; i < data.allLabels.length; i++) {
|
|
142
|
+
for (let j = i + 1; j < data.allLabels.length; j++) {
|
|
143
|
+
const a = data.allLabels[i], b = data.allLabels[j];
|
|
144
|
+
if (a.owner === b.owner) continue;
|
|
145
|
+
if (overlaps1D(a.x, a.x + a.w, b.x, b.x + b.w, TOL) &&
|
|
146
|
+
overlaps1D(a.y, a.y + a.h, b.y, b.y + b.h, TOL)) labelLabel++;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
122
149
|
|
|
123
150
|
await page.screenshot({ path: `/tmp/anet-issue-112/overlap-${layout}.png` });
|
|
124
151
|
await ctx.close();
|
|
125
|
-
const ok = nodeNode === 0 && nodePanel === 0 && nodeLabel === 0 && boxBox === 0;
|
|
152
|
+
const ok = nodeNode === 0 && nodePanel === 0 && nodeLabel === 0 && boxBox === 0 && labelLabel === 0;
|
|
126
153
|
console.log(`${ok ? '✅' : '❌'} [${layout}] nodes=${data.nodes.length} groups=${data.groups.length} — ` +
|
|
127
|
-
`node↔node:${nodeNode} node↔panel:${nodePanel} node↔label:${nodeLabel} box↔box:${boxBox}`);
|
|
154
|
+
`node↔node:${nodeNode} node↔panel:${nodePanel} node↔label:${nodeLabel} box↔box:${boxBox} label↔label:${labelLabel}`);
|
|
128
155
|
return ok;
|
|
129
156
|
}
|
|
130
157
|
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/* Round 21 verification: SVG cursor flips from grab → grabbing during
|
|
2
|
+
* a pan drag and back to grab on release. */
|
|
3
|
+
import { chromium } from 'playwright';
|
|
4
|
+
import { readFileSync } from 'node:fs';
|
|
5
|
+
|
|
6
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
7
|
+
const browser = await chromium.launch({ headless: true });
|
|
8
|
+
// Need a tall enough viewport to keep the SVG ABOVE the fold so real
|
|
9
|
+
// pointer events land — at 1280×900 the canvas starts at ~y=815 (the
|
|
10
|
+
// /admin hero + StatsBar above push it below the viewport bottom).
|
|
11
|
+
const ctx = await browser.newContext({ viewport: { width: 1280, height: 1600 } });
|
|
12
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
13
|
+
await ctx.addInitScript(() => {
|
|
14
|
+
try {
|
|
15
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
16
|
+
localStorage.setItem('anet-topo-layout', 'grid');
|
|
17
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
18
|
+
} catch {}
|
|
19
|
+
});
|
|
20
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
21
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
22
|
+
const r = await route.fetch();
|
|
23
|
+
const b = await r.json();
|
|
24
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
25
|
+
const sessions = ['a', 'b'].map(a => ({
|
|
26
|
+
alias: a, status: 'idle', network_id: nid, project_dir: null,
|
|
27
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
28
|
+
}));
|
|
29
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
30
|
+
});
|
|
31
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
32
|
+
const page = await ctx.newPage();
|
|
33
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
34
|
+
await page.waitForSelector('svg[viewBox="0 0 1000 680"]', { timeout: 30000 });
|
|
35
|
+
await page.waitForTimeout(400);
|
|
36
|
+
|
|
37
|
+
const cursorAt = () => page.$eval('svg[viewBox="0 0 1000 680"]', el => el.style.cursor);
|
|
38
|
+
const rest = await cursorAt();
|
|
39
|
+
|
|
40
|
+
// pick a pan-safe target far from any node — top-left empty canvas region
|
|
41
|
+
const box = await page.$eval('svg[viewBox="0 0 1000 680"]', el => el.getBoundingClientRect());
|
|
42
|
+
const sx = box.x + box.width * 0.08;
|
|
43
|
+
const sy = box.y + box.height * 0.40;
|
|
44
|
+
|
|
45
|
+
await page.mouse.move(sx, sy);
|
|
46
|
+
await page.mouse.down();
|
|
47
|
+
await page.mouse.move(sx + 40, sy + 30);
|
|
48
|
+
await page.waitForTimeout(50);
|
|
49
|
+
const during = await cursorAt();
|
|
50
|
+
await page.mouse.up();
|
|
51
|
+
await page.waitForTimeout(50);
|
|
52
|
+
const after = await cursorAt();
|
|
53
|
+
|
|
54
|
+
await browser.close();
|
|
55
|
+
const results = {
|
|
56
|
+
restIsGrab: rest === 'grab',
|
|
57
|
+
duringIsGrabbing: during === 'grabbing',
|
|
58
|
+
afterIsGrab: after === 'grab',
|
|
59
|
+
};
|
|
60
|
+
const ok = Object.values(results).every(Boolean);
|
|
61
|
+
console.log(`${ok ? '✅' : '❌'} pan cursor:`, JSON.stringify(results), `rest=${rest} during=${during} after=${after}`);
|
|
62
|
+
process.exit(ok ? 0 : 1);
|