@sleep2agi/agent-network-dashboard 0.4.6-preview.21 → 0.4.6-preview.211
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/05uk96gc~9mni.js +1 -0
- package/.next/static/chunks/08kf-.bgs10ze.js +1 -0
- package/.next/static/chunks/09mo5s0d1t.rl.js +4 -0
- package/.next/static/chunks/0_tufsuno-ybu.js +1 -0
- package/.next/static/chunks/0ggj5ummw2a84.js +7 -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/0yjst6n4eqy4c.js +1 -0
- package/.next/static/chunks/0~k1yvhakbob6.css +2 -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 +126 -0
- package/app/components/ServersDrawer.tsx +64 -27
- package/app/components/TopoGraph.tsx +4228 -255
- package/app/components/utils.ts +8 -7
- package/app/globals.css +110 -5
- 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-self-hover-test.mjs +171 -0
- package/scripts/topo-active-links-chip-test.mjs +73 -0
- package/scripts/topo-active-links-freshness-test.mjs +113 -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-canvas-aria-test.mjs +108 -0
- package/scripts/topo-chat-target-breath-test.mjs +84 -0
- package/scripts/topo-chat-target-ring-fade-test.mjs +124 -0
- package/scripts/topo-chip-focus-test.mjs +95 -0
- package/scripts/topo-chip-pin-mirror-test.mjs +113 -0
- package/scripts/topo-chrome-a11y-test.mjs +107 -0
- package/scripts/topo-chrome-press-state-test.mjs +135 -0
- package/scripts/topo-chrome-zoom-level-pop-test.mjs +122 -0
- package/scripts/topo-chrome-zoom-pop-test.mjs +130 -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-discrete-zoom-smooth-test.mjs +125 -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-badge-lift-test.mjs +150 -0
- package/scripts/topo-edge-badge-ripple-test.mjs +111 -0
- package/scripts/topo-edge-badge-stroke-transition-test.mjs +118 -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-fadein-test.mjs +106 -0
- package/scripts/topo-edge-focus-test.mjs +113 -0
- package/scripts/topo-edge-hover-test.mjs +93 -0
- package/scripts/topo-edge-pill-hot-test.mjs +129 -0
- package/scripts/topo-edge-pill-test.mjs +105 -0
- package/scripts/topo-edge-stroke-transition-test.mjs +132 -0
- package/scripts/topo-edge-tooltip-test.mjs +70 -0
- package/scripts/topo-empty-state-breath-test.mjs +150 -0
- package/scripts/topo-endpoint-ring-fade-test.mjs +132 -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-freshness-chip-transition-test.mjs +80 -0
- package/scripts/topo-fullscreen-active-test.mjs +115 -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-a11y-test.mjs +113 -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-fadein-test.mjs +95 -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-r153-test.mjs +85 -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-a11y-test.mjs +112 -0
- package/scripts/topo-hub-busyness-test.mjs +100 -0
- package/scripts/topo-hub-click-fit-test.mjs +137 -0
- package/scripts/topo-hub-fadein-test.mjs +88 -0
- package/scripts/topo-hub-hover-lift-test.mjs +101 -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-layout-switch-crossfade-test.mjs +126 -0
- package/scripts/topo-layout-toggle-test.mjs +131 -0
- package/scripts/topo-legend-count-empty-test.mjs +126 -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-ring-fade-test.mjs +112 -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-legend-row-title-test.mjs +102 -0
- package/scripts/topo-legend-swatch-grow-test.mjs +116 -0
- package/scripts/topo-minimap-a11y-test.mjs +101 -0
- package/scripts/topo-minimap-dot-transition-test.mjs +113 -0
- package/scripts/topo-minimap-test.mjs +68 -0
- package/scripts/topo-minimap-viewport-smooth-test.mjs +124 -0
- package/scripts/topo-mobile-chips-test.mjs +103 -0
- package/scripts/topo-node-a11y-test.mjs +107 -0
- package/scripts/topo-node-hover-lift-test.mjs +85 -0
- package/scripts/topo-node-label-card-elevation-test.mjs +157 -0
- package/scripts/topo-node-stroke-transition-test.mjs +103 -0
- package/scripts/topo-node-title-peers-test.mjs +107 -0
- package/scripts/topo-node-title-test.mjs +101 -0
- package/scripts/topo-nodesize-active-hover-test.mjs +109 -0
- package/scripts/topo-nodesize-crossfade-test.mjs +141 -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-fadein-test.mjs +102 -0
- package/scripts/topo-panel-hot-tail-fade-test.mjs +124 -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-mirror-transition-test.mjs +101 -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-pressure-seg-transition-test.mjs +107 -0
- package/scripts/topo-pulse-rate-test.mjs +71 -0
- package/scripts/topo-recent-panel-count-freshness-test.mjs +109 -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-cyan-hover-test.mjs +151 -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-count-fill-test.mjs +129 -0
- package/scripts/topo-recent-row-fadein-test.mjs +107 -0
- package/scripts/topo-recent-row-freshness-test.mjs +102 -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-title-test.mjs +112 -0
- package/scripts/topo-recent-row-ts-alpha-test.mjs +100 -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-reset-spin-test.mjs +120 -0
- package/scripts/topo-spoke-busyness-test.mjs +110 -0
- package/scripts/topo-status-chip-hover-test.mjs +101 -0
- package/scripts/topo-status-chip-tooltip-test.mjs +95 -0
- package/scripts/topo-statusbar-chips-self-hover-test.mjs +177 -0
- package/scripts/topo-svg-focus-test.mjs +112 -0
- package/scripts/topo-sweep-busyness-test.mjs +94 -0
- package/scripts/topo-tier-ring-fadein-test.mjs +107 -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-chip-self-hover-test.mjs +130 -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-view-smooth-test.mjs +131 -0
- package/scripts/topo-wheel-zoom-test.mjs +79 -0
- package/scripts/topo-working-chip-pin-test.mjs +154 -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/01j59m93~ijpq.js +0 -1
- package/.next/static/chunks/02t5jr4shh.hy.js +0 -1
- package/.next/static/chunks/09nd.t_hd.y6t.css +0 -2
- package/.next/static/chunks/0_qfg5s5uzs7..js +0 -7
- 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/0ms~5~zq93-vs.js +0 -1
- package/.next/static/chunks/0nh4tljy5z5ns.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/{XiA2VWgHsZvuY-5yWxSPj → FMKI0Nb4m8uK25F_8komz}/_buildManifest.js +0 -0
- /package/.next/static/{XiA2VWgHsZvuY-5yWxSPj → FMKI0Nb4m8uK25F_8komz}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{XiA2VWgHsZvuY-5yWxSPj → FMKI0Nb4m8uK25F_8komz}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/* Round 136 verification: the "N active links" chip in the chip
|
|
2
|
+
* row becomes a clickable nav to /messages when flowLinks.length
|
|
3
|
+
* > 0. Closes the cursor-pointer-lying bug — the chip already had
|
|
4
|
+
* cursor:pointer set when interactive, but no onClick handler was
|
|
5
|
+
* wired (line 1877 of TopoGraph.tsx pre-R136). Mirrors R133 footer-
|
|
6
|
+
* nav idiom and generalizes "click to open the full flow list"
|
|
7
|
+
* across the chip row, not just the recent-signal panel.
|
|
8
|
+
*
|
|
9
|
+
* Two-state behavior:
|
|
10
|
+
* - flowLinks.length === 0 → not interactive: no role=link, no
|
|
11
|
+
* cursor:pointer, no onClick fires
|
|
12
|
+
* - flowLinks.length > 0 → role=link, tabIndex=0, cursor:pointer,
|
|
13
|
+
* onClick → /messages, tooltip mentions "click to open"
|
|
14
|
+
*
|
|
15
|
+
* Hover still boosts edges (R77) regardless — that's a separate
|
|
16
|
+
* gesture.
|
|
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 probeWithFlows(pairCount) {
|
|
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
|
+
const aliases = ['alpha', 'beta', 'gamma', 'delta', 'epsilon'];
|
|
32
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
33
|
+
const r = await route.fetch();
|
|
34
|
+
const b = await r.json();
|
|
35
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
36
|
+
const mk = (alias) => ({
|
|
37
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
38
|
+
network_id: nid, project_dir: null,
|
|
39
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
40
|
+
});
|
|
41
|
+
await route.fulfill({ response: r, json: { ...b, sessions: aliases.map(mk) } });
|
|
42
|
+
});
|
|
43
|
+
const now = Date.now();
|
|
44
|
+
const msgs = [];
|
|
45
|
+
for (let i = 0; i < pairCount; i++) {
|
|
46
|
+
msgs.push({
|
|
47
|
+
id: `m${i}`,
|
|
48
|
+
from_alias: aliases[i % aliases.length],
|
|
49
|
+
to_alias: aliases[(i + 1) % aliases.length],
|
|
50
|
+
content: 'hi',
|
|
51
|
+
network_id: 'default',
|
|
52
|
+
created_at: new Date(now - (20000 + i * 1000)).toISOString(),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
|
|
56
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
57
|
+
|
|
58
|
+
const page = await ctx.newPage();
|
|
59
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
60
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 5, { timeout: 30000 });
|
|
61
|
+
await page.waitForSelector('[data-active-links-chip]', { timeout: 10000 });
|
|
62
|
+
await page.waitForTimeout(400);
|
|
63
|
+
|
|
64
|
+
const probe = await page.evaluate(() => {
|
|
65
|
+
const chip = document.querySelector('[data-active-links-chip]');
|
|
66
|
+
return {
|
|
67
|
+
flowCount: chip?.getAttribute('data-active-links-flow-count'),
|
|
68
|
+
clickable: chip?.getAttribute('data-active-links-clickable'),
|
|
69
|
+
role: chip?.getAttribute('role'),
|
|
70
|
+
tabindex: chip?.getAttribute('tabindex'),
|
|
71
|
+
cursor: (chip?.getAttribute('style') || '').includes('cursor: pointer'),
|
|
72
|
+
title: chip?.getAttribute('title'),
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
let finalUrl = null;
|
|
77
|
+
if (pairCount > 0) {
|
|
78
|
+
await page.locator('[data-active-links-chip]').click();
|
|
79
|
+
await page.waitForURL('**/messages', { timeout: 10000 });
|
|
80
|
+
finalUrl = page.url();
|
|
81
|
+
}
|
|
82
|
+
await browser.close();
|
|
83
|
+
return { probe, finalUrl };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const empty = await probeWithFlows(0);
|
|
87
|
+
const oneFlow = await probeWithFlows(1);
|
|
88
|
+
const many = await probeWithFlows(3);
|
|
89
|
+
|
|
90
|
+
const results = {
|
|
91
|
+
// Empty (0 flows) — not interactive
|
|
92
|
+
empty_flowCount0: empty.probe.flowCount === '0',
|
|
93
|
+
empty_clickableFalse: empty.probe.clickable === 'false',
|
|
94
|
+
empty_noRole: empty.probe.role === null || empty.probe.role === undefined,
|
|
95
|
+
empty_noTabIndex: empty.probe.tabindex === null || empty.probe.tabindex === undefined,
|
|
96
|
+
empty_noPointer: !empty.probe.cursor,
|
|
97
|
+
empty_noTooltip: !empty.probe.title,
|
|
98
|
+
|
|
99
|
+
// 1 flow — interactive
|
|
100
|
+
one_flowCount1: oneFlow.probe.flowCount === '1',
|
|
101
|
+
one_clickableTrue: oneFlow.probe.clickable === 'true',
|
|
102
|
+
one_roleLink: oneFlow.probe.role === 'link',
|
|
103
|
+
one_tabIndex0: oneFlow.probe.tabindex === '0',
|
|
104
|
+
one_cursorPointer: oneFlow.probe.cursor,
|
|
105
|
+
one_titleMentionsOpen: (oneFlow.probe.title || '').includes('click to open'),
|
|
106
|
+
one_navigatedToMessages: /\/messages$/.test(oneFlow.finalUrl || ''),
|
|
107
|
+
|
|
108
|
+
// 3 flows — same interactive shape
|
|
109
|
+
many_clickableTrue: many.probe.clickable === 'true',
|
|
110
|
+
many_navigatedToMessages: /\/messages$/.test(many.finalUrl || ''),
|
|
111
|
+
};
|
|
112
|
+
const ok = Object.values(results).every(Boolean);
|
|
113
|
+
console.log(`${ok ? '✅' : '❌'} active-links chip nav:`, JSON.stringify(results),
|
|
114
|
+
`\n empty=`, empty,
|
|
115
|
+
`\n oneFlow=`, oneFlow,
|
|
116
|
+
`\n many=`, many);
|
|
117
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/* Round 193 verification: the active-links chip itself responds to
|
|
2
|
+
* hover by adopting a cyan tint, mirroring the canvas-edge highlight
|
|
3
|
+
* it already fires (which R77's topo-active-links-chip-hover-test
|
|
4
|
+
* verifies). Two different surfaces:
|
|
5
|
+
* R77 test → canvas edges (effect element)
|
|
6
|
+
* R193 test → chip itself (cause element)
|
|
7
|
+
*
|
|
8
|
+
* Pre-R193 the chip stayed gray when hovered while the canvas edges
|
|
9
|
+
* brightened to cyan — cause-element silent, effect-element loud.
|
|
10
|
+
* R193 attaches a Tailwind :hover variant (cyan-500/10 bg, cyan-200
|
|
11
|
+
* text, cyan-500/30 border) when the chip is interactive, plus a
|
|
12
|
+
* transition-colors duration-200 for smooth blend. Non-interactive
|
|
13
|
+
* chips (0 active links) keep the gray-only variant.
|
|
14
|
+
*
|
|
15
|
+
* Test scenarios:
|
|
16
|
+
* A. Interactive (3 flows):
|
|
17
|
+
* 1. Idle: bg near gray-500/10 (R,G,B close to each other)
|
|
18
|
+
* 2. className includes 'hover:bg-cyan-500/10'
|
|
19
|
+
* 3. Hover: bg, text, border all shift cyan (B > R)
|
|
20
|
+
* 4. transition-colors duration-200 present
|
|
21
|
+
* 5. bg actually changed from idle to hover
|
|
22
|
+
* B. Non-interactive (0 flows):
|
|
23
|
+
* 6. className does NOT include 'hover:bg-cyan-500/10'
|
|
24
|
+
* 7. data-active-links-clickable='false'
|
|
25
|
+
*/
|
|
26
|
+
import { chromium } from 'playwright';
|
|
27
|
+
import { readFileSync } from 'node:fs';
|
|
28
|
+
|
|
29
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
30
|
+
const browser = await chromium.launch({ headless: true });
|
|
31
|
+
|
|
32
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
33
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
34
|
+
await ctx.addInitScript(() => {
|
|
35
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
async function load(flowsScenario) {
|
|
39
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
40
|
+
await ctx.unroute('**/api/hub/status*').catch(() => {});
|
|
41
|
+
await ctx.unroute('**/api/hub/messages*').catch(() => {});
|
|
42
|
+
await ctx.unroute('**/api/hub/tasks*').catch(() => {});
|
|
43
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
44
|
+
const r = await route.fetch();
|
|
45
|
+
const b = await r.json();
|
|
46
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
47
|
+
const mk = (alias) => ({
|
|
48
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
49
|
+
network_id: nid, project_dir: null,
|
|
50
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
51
|
+
});
|
|
52
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
53
|
+
mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
|
|
54
|
+
] } });
|
|
55
|
+
});
|
|
56
|
+
const now = Date.now();
|
|
57
|
+
const msgs = flowsScenario(now);
|
|
58
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
|
|
59
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
60
|
+
|
|
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.waitForSelector('[data-active-links-chip]', { timeout: 10000 });
|
|
65
|
+
await page.waitForTimeout(400);
|
|
66
|
+
return page;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Scenario A: 3 flows (interactive)
|
|
70
|
+
const pageA = await load((now) => [
|
|
71
|
+
{ id: '1', from_alias: 'alpha', to_alias: 'beta', content: 'hi',
|
|
72
|
+
network_id: 'default', created_at: new Date(now - 5000).toISOString() },
|
|
73
|
+
{ id: '2', from_alias: 'beta', to_alias: 'gamma', content: 'hi',
|
|
74
|
+
network_id: 'default', created_at: new Date(now - 8000).toISOString() },
|
|
75
|
+
{ id: '3', from_alias: 'gamma', to_alias: 'delta', content: 'hi',
|
|
76
|
+
network_id: 'default', created_at: new Date(now - 11000).toISOString() },
|
|
77
|
+
]);
|
|
78
|
+
|
|
79
|
+
const idleA = await pageA.evaluate(() => {
|
|
80
|
+
const el = document.querySelector('[data-active-links-chip]');
|
|
81
|
+
const cs = getComputedStyle(el);
|
|
82
|
+
return {
|
|
83
|
+
className: el.getAttribute('class') || '',
|
|
84
|
+
clickable: el.getAttribute('data-active-links-clickable'),
|
|
85
|
+
flowCount: el.getAttribute('data-active-links-flow-count'),
|
|
86
|
+
bg: cs.backgroundColor,
|
|
87
|
+
color: cs.color,
|
|
88
|
+
borderColor:cs.borderTopColor,
|
|
89
|
+
transition: cs.transitionProperty + ' / ' + cs.transitionDuration,
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
await pageA.hover('[data-active-links-chip]');
|
|
94
|
+
await pageA.waitForTimeout(280); // longer than 200ms transition
|
|
95
|
+
|
|
96
|
+
const hoverA = await pageA.evaluate(() => {
|
|
97
|
+
const el = document.querySelector('[data-active-links-chip]');
|
|
98
|
+
const cs = getComputedStyle(el);
|
|
99
|
+
return {
|
|
100
|
+
bg: cs.backgroundColor,
|
|
101
|
+
color: cs.color,
|
|
102
|
+
borderColor:cs.borderTopColor,
|
|
103
|
+
};
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
await pageA.mouse.move(0, 0);
|
|
107
|
+
await pageA.waitForTimeout(50);
|
|
108
|
+
await pageA.close();
|
|
109
|
+
|
|
110
|
+
// Scenario B: 0 flows (non-interactive)
|
|
111
|
+
const pageB = await load(() => []);
|
|
112
|
+
const idleB = await pageB.evaluate(() => {
|
|
113
|
+
const el = document.querySelector('[data-active-links-chip]');
|
|
114
|
+
return {
|
|
115
|
+
className: el.getAttribute('class') || '',
|
|
116
|
+
clickable: el.getAttribute('data-active-links-clickable'),
|
|
117
|
+
flowCount: el.getAttribute('data-active-links-flow-count'),
|
|
118
|
+
};
|
|
119
|
+
});
|
|
120
|
+
await pageB.close();
|
|
121
|
+
await browser.close();
|
|
122
|
+
|
|
123
|
+
// Tailwind 4 emits modern color spaces (oklab / lab). Cyan in OKLab
|
|
124
|
+
// is characterised by a<0 (green-leaning) AND b<0 (blue-leaning).
|
|
125
|
+
// Gray has a ≈ 0, b ≈ 0. Lab uses similar axes at different scales.
|
|
126
|
+
function parseLab(s) {
|
|
127
|
+
const m = (s || '').match(/(?:oklab|lab)\(\s*([\-\d.]+)\s+([\-\d.]+)\s+([\-\d.]+)/);
|
|
128
|
+
return m ? { L: +m[1], a: +m[2], b: +m[3] } : null;
|
|
129
|
+
}
|
|
130
|
+
const idleBgLab = parseLab(idleA.bg);
|
|
131
|
+
const hoverBgLab = parseLab(hoverA.bg);
|
|
132
|
+
const hoverFgLab = parseLab(hoverA.color);
|
|
133
|
+
const hoverBdLab = parseLab(hoverA.borderColor);
|
|
134
|
+
const idleFgLab = parseLab(idleA.color);
|
|
135
|
+
|
|
136
|
+
// Cyan-leaning thresholds: a more negative by at least 0.05 (OKLab)
|
|
137
|
+
// or 5 (Lab), b similarly. Robust to either color space.
|
|
138
|
+
const isMoreCyan = (idle, hov) =>
|
|
139
|
+
hov && idle && (hov.a < idle.a - 0.04 || hov.b < idle.b - 0.04 || hov.a < -0.05 || hov.b < -0.05);
|
|
140
|
+
|
|
141
|
+
const results = {
|
|
142
|
+
// A1: idle is roughly gray-ish — small a,b deviation from 0
|
|
143
|
+
idleA_gray_bg: idleBgLab && Math.abs(idleBgLab.a) < 0.05 && Math.abs(idleBgLab.b) < 0.05,
|
|
144
|
+
|
|
145
|
+
// A2: className has hover variant
|
|
146
|
+
idleA_has_hover: idleA.className.includes('hover:bg-cyan-500/10'),
|
|
147
|
+
|
|
148
|
+
// A3: hovered surfaces lean cyan vs idle
|
|
149
|
+
hover_bg_cyan: isMoreCyan(idleBgLab, hoverBgLab),
|
|
150
|
+
hover_text_cyan: isMoreCyan(idleFgLab, hoverFgLab),
|
|
151
|
+
hover_border_cyan: isMoreCyan(idleBgLab, hoverBdLab),
|
|
152
|
+
|
|
153
|
+
// bg actually changed
|
|
154
|
+
bg_changed: idleA.bg !== hoverA.bg,
|
|
155
|
+
|
|
156
|
+
// A4: transition-colors duration-200 present
|
|
157
|
+
idleA_has_transition: idleA.transition.includes('color') && idleA.transition.includes('0.2s'),
|
|
158
|
+
idleA_clickable_true: idleA.clickable === 'true',
|
|
159
|
+
idleA_flow_count_3: idleA.flowCount === '3',
|
|
160
|
+
|
|
161
|
+
// B6: non-interactive chip has NO hover variant
|
|
162
|
+
idleB_no_hover: !idleB.className.includes('hover:bg-cyan-500/10'),
|
|
163
|
+
idleB_clickable_false:idleB.clickable === 'false',
|
|
164
|
+
idleB_flow_count_0: idleB.flowCount === '0',
|
|
165
|
+
};
|
|
166
|
+
const ok = Object.values(results).every(Boolean);
|
|
167
|
+
console.log(`${ok ? '✅' : '❌'} active-links chip self-hover:`, JSON.stringify(results),
|
|
168
|
+
`\n idleA:`, idleA,
|
|
169
|
+
`\n hoverA:`, hoverA,
|
|
170
|
+
`\n idleB:`, idleB);
|
|
171
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/* Round 42 verification: "X active links" chip in the header includes
|
|
2
|
+
* "· last <relative>" when flow links exist, drops the `· last …` tail
|
|
3
|
+
* when no flow links are present, and renders singular/plural correctly. */
|
|
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
|
+
async function probe({ msgs }) {
|
|
11
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1000 } });
|
|
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 = ['alpha', 'beta', 'gamma'].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/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
|
|
32
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
33
|
+
const page = await ctx.newPage();
|
|
34
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
35
|
+
await page.waitForSelector('svg[viewBox="0 0 1000 680"]', { timeout: 30000 });
|
|
36
|
+
await page.waitForTimeout(500);
|
|
37
|
+
const text = await page.evaluate(() => {
|
|
38
|
+
// The active-links chip is the LAST chip in the topo header row (after
|
|
39
|
+
// pressure-bar + vendor + working/online). Match by `active link`.
|
|
40
|
+
const spans = [...document.querySelectorAll('span')];
|
|
41
|
+
return spans.find(s => /active link/.test(s.textContent || ''))?.textContent?.replace(/\s+/g, ' ').trim() || null;
|
|
42
|
+
});
|
|
43
|
+
await ctx.close();
|
|
44
|
+
return text;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 1. No messages → 0 active links, no "· last X" tail
|
|
48
|
+
const empty = await probe({ msgs: [] });
|
|
49
|
+
// 2. One message just now → 1 active link · last Ns ago
|
|
50
|
+
const recent = await probe({ msgs: [
|
|
51
|
+
{ from_alias: 'alpha', to_alias: 'beta', content: 'hi', created_at: new Date(Date.now() - 30 * 1000).toISOString() },
|
|
52
|
+
] });
|
|
53
|
+
// 3. Five messages, latest 4 min ago → 5 active links · last 4m ago (singular link uses "link", plural "links")
|
|
54
|
+
const fourMinAgo = new Date(Date.now() - 4 * 60 * 1000).toISOString();
|
|
55
|
+
const eightMinAgo = new Date(Date.now() - 8 * 60 * 1000).toISOString();
|
|
56
|
+
const five = await probe({ msgs: [
|
|
57
|
+
{ from_alias: 'alpha', to_alias: 'beta', content: '1', created_at: eightMinAgo },
|
|
58
|
+
{ from_alias: 'alpha', to_alias: 'gamma', content: '2', created_at: eightMinAgo },
|
|
59
|
+
{ from_alias: 'alpha', to_alias: 'gamma', content: '3', created_at: eightMinAgo },
|
|
60
|
+
{ from_alias: 'beta', to_alias: 'gamma', content: '4', created_at: eightMinAgo },
|
|
61
|
+
{ from_alias: 'beta', to_alias: 'gamma', content: '5', created_at: fourMinAgo }, // latest
|
|
62
|
+
] });
|
|
63
|
+
|
|
64
|
+
await browser.close();
|
|
65
|
+
const results = {
|
|
66
|
+
emptyChipHasNoTail: empty === '0 active links',
|
|
67
|
+
recentSingularChip: recent !== null && /^1 active link · last \d+s ago$/.test(recent),
|
|
68
|
+
pluralChipShowsLatest: five !== null && /active links · last [34]m ago/.test(five),
|
|
69
|
+
};
|
|
70
|
+
const ok = Object.values(results).every(Boolean);
|
|
71
|
+
console.log(`${ok ? '✅' : '❌'} active-links chip:`, JSON.stringify(results),
|
|
72
|
+
`\n empty=${JSON.stringify(empty)}\n recent=${JSON.stringify(recent)}\n five=${JSON.stringify(five)}`);
|
|
73
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/* Round 161 verification: active-links chip bullet picks up
|
|
2
|
+
* the R160 freshness vocabulary.
|
|
3
|
+
*
|
|
4
|
+
* R10 fades canvas flow edges by freshness. R160 brought that
|
|
5
|
+
* vocabulary to the recent-signal panel rows via a 1.6-px cyan
|
|
6
|
+
* pip at row left edge. R161 extends it one more scope — up to
|
|
7
|
+
* the fleet-aggregate active-links chip in the chip-row.
|
|
8
|
+
*
|
|
9
|
+
* The chip text was already "3 active links · last 5s ago" but
|
|
10
|
+
* the " · " separator was dead gray. R161 colors that bullet
|
|
11
|
+
* by freshness using the same alpha ramp:
|
|
12
|
+
* ageSec ≤ 30 → 1.0 (fully fresh)
|
|
13
|
+
* 30-300s → 1.0 → 0.25 smooth decay
|
|
14
|
+
* > 300s → 0.25 stale floor
|
|
15
|
+
*
|
|
16
|
+
* Three nested scopes now share one freshness ladder:
|
|
17
|
+
* canvas edge fade (R10) — per-edge visual
|
|
18
|
+
* row pip (R160) — per-flow row in recent-signal
|
|
19
|
+
* chip bullet (R161) — fleet aggregate in chip-row
|
|
20
|
+
*
|
|
21
|
+
* Test scenarios (one page load each):
|
|
22
|
+
* A. fresh most-recent flow (5s ago) → alpha ≈ 1.0, fontWeight 700
|
|
23
|
+
* B. mid recency (90s ago) → alpha ≈ 0.83
|
|
24
|
+
* C. stale (360s ago) → alpha ≈ 0.25 (floor)
|
|
25
|
+
*
|
|
26
|
+
* Color parses out of `rgba(34, 211, 238, 1)` cyber theme.
|
|
27
|
+
*/
|
|
28
|
+
import { chromium } from 'playwright';
|
|
29
|
+
import { readFileSync } from 'node:fs';
|
|
30
|
+
|
|
31
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
32
|
+
const browser = await chromium.launch({ headless: true });
|
|
33
|
+
|
|
34
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
35
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
36
|
+
await ctx.addInitScript(() => {
|
|
37
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
async function probe(msgAgeSec) {
|
|
41
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
42
|
+
await ctx.unroute('**/api/hub/status*').catch(() => {});
|
|
43
|
+
await ctx.unroute('**/api/hub/messages*').catch(() => {});
|
|
44
|
+
await ctx.unroute('**/api/hub/tasks*').catch(() => {});
|
|
45
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
46
|
+
const r = await route.fetch();
|
|
47
|
+
const b = await r.json();
|
|
48
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
49
|
+
const mk = (alias) => ({
|
|
50
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
51
|
+
network_id: nid, project_dir: null,
|
|
52
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
53
|
+
});
|
|
54
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta')] } });
|
|
55
|
+
});
|
|
56
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
|
|
57
|
+
{ id: 'x', from_alias: 'alpha', to_alias: 'beta', content: 'hi',
|
|
58
|
+
network_id: 'default', created_at: new Date(Date.now() - msgAgeSec * 1000).toISOString() },
|
|
59
|
+
] } }));
|
|
60
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
61
|
+
|
|
62
|
+
const page = await ctx.newPage();
|
|
63
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
64
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 2, { timeout: 30000 });
|
|
65
|
+
await page.waitForSelector('[data-active-links-freshness-dot]', { timeout: 10000 });
|
|
66
|
+
await page.waitForTimeout(400);
|
|
67
|
+
const out = await page.evaluate(() => {
|
|
68
|
+
const el = document.querySelector('[data-active-links-freshness-dot]');
|
|
69
|
+
if (!el) return null;
|
|
70
|
+
const styleColor = el.style.color || getComputedStyle(el).color;
|
|
71
|
+
return {
|
|
72
|
+
alpha: parseFloat(el.getAttribute('data-active-links-freshness-alpha') || ''),
|
|
73
|
+
color: styleColor,
|
|
74
|
+
fontWeight: el.style.fontWeight || getComputedStyle(el).fontWeight,
|
|
75
|
+
text: el.textContent,
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
await page.close();
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const A = await probe(5); // fresh
|
|
83
|
+
const B = await probe(90); // mid
|
|
84
|
+
const C = await probe(360); // stale
|
|
85
|
+
|
|
86
|
+
await browser.close();
|
|
87
|
+
|
|
88
|
+
const cyanRgb = (str) => {
|
|
89
|
+
// Inline `style="color: rgba(34, 211, 238, ...)"` — alpha is part of color.
|
|
90
|
+
const m = /rgba?\((\d+),\s*(\d+),\s*(\d+)/.exec(str);
|
|
91
|
+
if (!m) return null;
|
|
92
|
+
return [Number(m[1]), Number(m[2]), Number(m[3])];
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const results = {
|
|
96
|
+
fresh_dot_found: A !== null,
|
|
97
|
+
mid_dot_found: B !== null,
|
|
98
|
+
stale_dot_found: C !== null,
|
|
99
|
+
fresh_alpha_1: A && Math.abs(A.alpha - 1.0) < 0.05,
|
|
100
|
+
mid_alpha_window: B && B.alpha > 0.7 && B.alpha < 0.95,
|
|
101
|
+
stale_alpha_floor: C && Math.abs(C.alpha - 0.25) < 0.05,
|
|
102
|
+
fresh_color_cyan: A && JSON.stringify(cyanRgb(A.color)) === '[34,211,238]',
|
|
103
|
+
fresh_weight_bold: A && (A.fontWeight === '700' || A.fontWeight === 'bold'),
|
|
104
|
+
stale_weight_norm: C && (C.fontWeight === '400' || C.fontWeight === 'normal' || C.fontWeight === ''),
|
|
105
|
+
ladder_descends: A && B && C && A.alpha > B.alpha && B.alpha > C.alpha,
|
|
106
|
+
text_is_separator: A && A.text && A.text.trim() === '·',
|
|
107
|
+
};
|
|
108
|
+
const ok = Object.values(results).every(Boolean);
|
|
109
|
+
console.log(`${ok ? '✅' : '❌'} active-links freshness:`, JSON.stringify(results),
|
|
110
|
+
`\n fresh =`, A,
|
|
111
|
+
`\n mid =`, B,
|
|
112
|
+
`\n stale =`, C);
|
|
113
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/* Round 114 verification: active-links chip tooltip lists the actual
|
|
2
|
+
* flow pairs. Closes the info-density sweep on the last chip-row
|
|
3
|
+
* hover surface (R97-R113 covered everything else). Format:
|
|
4
|
+
* "alpha→beta (3), gamma→delta (1) — hover brightens all"
|
|
5
|
+
*
|
|
6
|
+
* Drive 3 flow pairs of different counts. Verify each pair appears
|
|
7
|
+
* in the title with its count; the trailing action hint says
|
|
8
|
+
* "hover brightens all". Empty-flow fleet → no title.
|
|
9
|
+
*/
|
|
10
|
+
import { chromium } from 'playwright';
|
|
11
|
+
import { readFileSync } from 'node:fs';
|
|
12
|
+
|
|
13
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
14
|
+
const browser = await chromium.launch({ headless: true });
|
|
15
|
+
const fresh = new Date(Date.now() - 30 * 1000).toISOString();
|
|
16
|
+
|
|
17
|
+
const probe = async (withFlow) => {
|
|
18
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
|
|
19
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
20
|
+
await ctx.addInitScript(() => {
|
|
21
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
22
|
+
});
|
|
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 mk = (alias) => ({
|
|
28
|
+
alias, status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
29
|
+
network_id: nid, project_dir: null,
|
|
30
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
31
|
+
});
|
|
32
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta'), mk('gamma'), mk('delta')] } });
|
|
33
|
+
});
|
|
34
|
+
const now = Date.now();
|
|
35
|
+
const mkMsg = (id, from_alias, to_alias, ageMs) => ({
|
|
36
|
+
id, from_alias, to_alias, content: 'hi', network_id: 'default',
|
|
37
|
+
created_at: new Date(now - ageMs).toISOString(),
|
|
38
|
+
});
|
|
39
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: withFlow ? [
|
|
40
|
+
mkMsg('m1', 'alpha', 'beta', 30 * 1000),
|
|
41
|
+
mkMsg('m2', 'alpha', 'beta', 40 * 1000),
|
|
42
|
+
mkMsg('m3', 'alpha', 'beta', 50 * 1000),
|
|
43
|
+
mkMsg('m4', 'gamma', 'delta', 60 * 1000),
|
|
44
|
+
] : [] } }));
|
|
45
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
46
|
+
const page = await ctx.newPage();
|
|
47
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
48
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
49
|
+
await page.waitForTimeout(600);
|
|
50
|
+
const data = await page.evaluate(() => {
|
|
51
|
+
const el = document.querySelector('[data-active-links-chip]');
|
|
52
|
+
return {
|
|
53
|
+
title: el?.getAttribute('title'),
|
|
54
|
+
flowCount: el?.getAttribute('data-active-links-flow-count') || '',
|
|
55
|
+
text: el?.textContent?.trim() || '',
|
|
56
|
+
};
|
|
57
|
+
});
|
|
58
|
+
await ctx.close();
|
|
59
|
+
return data;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const empty = await probe(false);
|
|
63
|
+
const full = await probe(true);
|
|
64
|
+
await browser.close();
|
|
65
|
+
|
|
66
|
+
const results = {
|
|
67
|
+
empty_noTitle: empty.title === null,
|
|
68
|
+
empty_zeroCount: empty.flowCount === '0',
|
|
69
|
+
full_titleHasAlphaBeta: /alpha→beta \(3\)/.test(full.title || ''),
|
|
70
|
+
full_titleHasGammaDelta: /gamma→delta \(1\)/.test(full.title || ''),
|
|
71
|
+
full_titleActionHint: /hover brightens all/.test(full.title || ''),
|
|
72
|
+
full_flowCount2: full.flowCount === '2',
|
|
73
|
+
full_chipShowsCount: /2 active link/.test(full.text),
|
|
74
|
+
};
|
|
75
|
+
const ok = Object.values(results).every(Boolean);
|
|
76
|
+
console.log(`${ok ? '✅' : '❌'} active-links tooltip:`, JSON.stringify(results),
|
|
77
|
+
`\n empty=`, empty,
|
|
78
|
+
`\n full=`, full);
|
|
79
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/* Round 75 verification: each fresh flow edge gets an "arrival ping"
|
|
2
|
+
* circle at the destination, synchronised to the particle period via
|
|
3
|
+
* `begin = -0.92 * dur`. Stale edges (fresh ≤ 0.5) skip the ping;
|
|
4
|
+
* reduced-motion users see none at all.
|
|
5
|
+
*
|
|
6
|
+
* - Fresh edge alpha→beta (created now) + stale edge gamma→delta (5 min
|
|
7
|
+
* old) → ping[alpha->beta] present, ping[gamma->delta] absent.
|
|
8
|
+
* - Reduced-motion context: no ping anywhere.
|
|
9
|
+
*/
|
|
10
|
+
import { chromium } from 'playwright';
|
|
11
|
+
import { readFileSync } from 'node:fs';
|
|
12
|
+
|
|
13
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
14
|
+
const browser = await chromium.launch({ headless: true });
|
|
15
|
+
|
|
16
|
+
async function probe(reducedMotion) {
|
|
17
|
+
const ctx = await browser.newContext({
|
|
18
|
+
viewport: { width: 1280, height: 1500 },
|
|
19
|
+
reducedMotion: reducedMotion ? 'reduce' : 'no-preference',
|
|
20
|
+
});
|
|
21
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
22
|
+
await ctx.addInitScript(() => {
|
|
23
|
+
try {
|
|
24
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
25
|
+
localStorage.setItem('anet-topo-layout', 'ring');
|
|
26
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
27
|
+
sessionStorage.removeItem('anet-topo-pinned-status');
|
|
28
|
+
sessionStorage.removeItem('anet-topo-pinned-group');
|
|
29
|
+
} catch {}
|
|
30
|
+
});
|
|
31
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
32
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
33
|
+
const r = await route.fetch();
|
|
34
|
+
const b = await r.json();
|
|
35
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
36
|
+
const sessions = ['alpha', 'beta', 'gamma', 'delta'].map(a => ({
|
|
37
|
+
alias: a, status: 'idle', network_id: nid, project_dir: null,
|
|
38
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
39
|
+
}));
|
|
40
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
41
|
+
});
|
|
42
|
+
// alpha→beta fresh (0s ago); gamma→delta stale (6 min ago) so fresh
|
|
43
|
+
// value drops below 0.5 (the 5-min half-life is roughly 0.4 at 6min).
|
|
44
|
+
const now = new Date().toISOString();
|
|
45
|
+
const old = new Date(Date.now() - 6 * 60 * 1000).toISOString();
|
|
46
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
|
|
47
|
+
{ from_alias: 'alpha', to_alias: 'beta', content: 'm', created_at: now },
|
|
48
|
+
{ from_alias: 'gamma', to_alias: 'delta', content: 'm', created_at: old },
|
|
49
|
+
] } }));
|
|
50
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
51
|
+
const page = await ctx.newPage();
|
|
52
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
53
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
54
|
+
await page.waitForTimeout(600);
|
|
55
|
+
const pings = await page.evaluate(() => {
|
|
56
|
+
const out = [];
|
|
57
|
+
for (const el of document.querySelectorAll('[data-arrival-ping]')) {
|
|
58
|
+
out.push({
|
|
59
|
+
key: el.getAttribute('data-arrival-ping'),
|
|
60
|
+
cx: el.getAttribute('cx'),
|
|
61
|
+
cy: el.getAttribute('cy'),
|
|
62
|
+
stroke: el.getAttribute('stroke'),
|
|
63
|
+
animateCount: el.querySelectorAll('animate').length,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
});
|
|
68
|
+
await ctx.close();
|
|
69
|
+
return pings;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const normal = await probe(false);
|
|
73
|
+
const reduced = await probe(true);
|
|
74
|
+
await browser.close();
|
|
75
|
+
|
|
76
|
+
const has = (rows, key) => rows.some(p => p.key === key);
|
|
77
|
+
const results = {
|
|
78
|
+
normal_freshPingPresent: has(normal, 'alpha->beta'),
|
|
79
|
+
normal_stalePingAbsent: !has(normal, 'gamma->delta'),
|
|
80
|
+
normal_twoAnimateChildren: normal.find(p => p.key === 'alpha->beta')?.animateCount === 2,
|
|
81
|
+
reduced_noPings: reduced.length === 0,
|
|
82
|
+
};
|
|
83
|
+
const ok = Object.values(results).every(Boolean);
|
|
84
|
+
console.log(`${ok ? '✅' : '❌'} arrival ping:`, JSON.stringify(results),
|
|
85
|
+
`\n normal=`, normal,
|
|
86
|
+
`\n reduced=`, reduced);
|
|
87
|
+
process.exit(ok ? 0 : 1);
|