@sleep2agi/agent-network-dashboard 0.4.6-preview.23 → 0.4.6-preview.231
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/0dpr42ixguifs.js +1 -0
- package/.next/static/chunks/0ggj5ummw2a84.js +7 -0
- package/.next/static/chunks/0glmrdj9112tc.js +7 -0
- package/.next/static/chunks/0rntk9_zs6l38.js +4 -0
- package/.next/static/chunks/0rxrez_cjn.eh.js +1 -0
- package/.next/static/chunks/0x8x.29hkju5a.js +1 -0
- package/.next/static/chunks/0~k1yvhakbob6.css +2 -0
- package/.next/static/chunks/11c3ptzm7ghp7.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 +126 -0
- package/app/components/ServersDrawer.tsx +64 -27
- package/app/components/TopoGraph.tsx +4568 -233
- 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-empty-recede-test.mjs +125 -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-empty-recede-test.mjs +116 -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-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-dense-alias-fill-transition-test.mjs +133 -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-crossfade-test.mjs +138 -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-badge-text-pin-test.mjs +121 -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-empty-state-crossfade-test.mjs +130 -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-pin-spacing-test.mjs +136 -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-pip-fadein-test.mjs +100 -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-hot-tail-crossfade-test.mjs +117 -0
- package/scripts/topo-hub-a11y-test.mjs +112 -0
- package/scripts/topo-hub-busyness-test.mjs +100 -0
- package/scripts/topo-hub-center-crossfade-test.mjs +124 -0
- package/scripts/topo-hub-click-fit-test.mjs +137 -0
- package/scripts/topo-hub-digit-scale-test.mjs +129 -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-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-pin-spacing-test.mjs +124 -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-label-card-stroke-test.mjs +135 -0
- package/scripts/topo-node-stroke-transition-test.mjs +103 -0
- package/scripts/topo-node-text-fill-transition-test.mjs +119 -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-orbit-opacity-bucket-test.mjs +124 -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 +132 -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-self-hover-test.mjs +126 -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-dot-crossfade-test.mjs +122 -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-crossfade-test.mjs +124 -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-spacing-test.mjs +137 -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-runtime-badge-grow-test.mjs +114 -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/01h.pqggl08cf.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/0r4q73.-1bkyl.js +0 -1
- package/.next/static/chunks/0r_c8eccg3z2o.js +0 -1
- package/.next/static/chunks/0vize6s4a808r.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/18dhhyoi_h..v.js +0 -1
- /package/.next/static/{ZgfN4dmEdRkfxqN6GWJeP → XCMqFGXs65DvNw_TGcR9u}/_buildManifest.js +0 -0
- /package/.next/static/{ZgfN4dmEdRkfxqN6GWJeP → XCMqFGXs65DvNw_TGcR9u}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{ZgfN4dmEdRkfxqN6GWJeP → XCMqFGXs65DvNw_TGcR9u}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/* Round 197 verification: legend swatch dot scales r 5.5 → 7 when
|
|
2
|
+
* its row is hovered or pinned. Mirrors R177 hub hover-lift but at
|
|
3
|
+
* legend grain.
|
|
4
|
+
*
|
|
5
|
+
* Pre-R197 the swatch was a flat constant r=5.5 — every other piece
|
|
6
|
+
* of the row responded to hover/pin (R55 text brighten, R105 bg
|
|
7
|
+
* tint, R144 1px row lift, R181 pin ring) but the swatch dot
|
|
8
|
+
* itself stayed still. The dot is the row's visual-identity
|
|
9
|
+
* anchor; making it grow gives the gesture full row-wide reach.
|
|
10
|
+
*
|
|
11
|
+
* Test (cyber/dark theme, 4 working sessions):
|
|
12
|
+
* 1. Three swatches present: working / idle / offline.
|
|
13
|
+
* 2. Idle: data-legend-swatch-state='idle', r ~ 5.5.
|
|
14
|
+
* 3. Hover the "working" legend row, wait past 150ms transition.
|
|
15
|
+
* 4. Working swatch: state='hover', r ~ 7.
|
|
16
|
+
* 5. Other swatches (idle/offline) stay 'idle' + r ~ 5.5.
|
|
17
|
+
* 6. Move away, wait — working swatch back to 'idle' + r 5.5.
|
|
18
|
+
* 7. transition: r 150ms ease-out is in the inline style.
|
|
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
|
+
const browser = await chromium.launch({ headless: true });
|
|
25
|
+
|
|
26
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
27
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
28
|
+
await ctx.addInitScript(() => {
|
|
29
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
33
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
34
|
+
const r = await route.fetch();
|
|
35
|
+
const b = await r.json();
|
|
36
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
37
|
+
const mk = (alias, status = 'working') => ({
|
|
38
|
+
alias, status, model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
39
|
+
network_id: nid, project_dir: null,
|
|
40
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
41
|
+
});
|
|
42
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
43
|
+
mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
|
|
44
|
+
] } });
|
|
45
|
+
});
|
|
46
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
47
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
48
|
+
|
|
49
|
+
const page = await ctx.newPage();
|
|
50
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
51
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
52
|
+
await page.waitForSelector('[data-legend-swatch]', { timeout: 10000 });
|
|
53
|
+
await page.waitForTimeout(300);
|
|
54
|
+
|
|
55
|
+
const probe = async () => page.evaluate(() => {
|
|
56
|
+
return [...document.querySelectorAll('[data-legend-swatch]')].map(el => ({
|
|
57
|
+
key: el.getAttribute('data-legend-swatch'),
|
|
58
|
+
state: el.getAttribute('data-legend-swatch-state'),
|
|
59
|
+
// Read both computed CSS r (the transitionable property) and the
|
|
60
|
+
// SVG attribute fallback. Chrome computes the CSS r as the live
|
|
61
|
+
// value, including mid-transition interpolated values.
|
|
62
|
+
cssR: parseFloat(getComputedStyle(el).r),
|
|
63
|
+
attrR: parseFloat(el.getAttribute('r') || ''),
|
|
64
|
+
transition: el.style.transition,
|
|
65
|
+
}));
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const idle = await probe();
|
|
69
|
+
|
|
70
|
+
await page.hover('[data-legend-status="working"]');
|
|
71
|
+
await page.waitForTimeout(220); // past 150ms r transition + margin
|
|
72
|
+
const hovered = await probe();
|
|
73
|
+
|
|
74
|
+
await page.mouse.move(0, 0);
|
|
75
|
+
await page.waitForTimeout(220);
|
|
76
|
+
const settled = await probe();
|
|
77
|
+
|
|
78
|
+
await browser.close();
|
|
79
|
+
|
|
80
|
+
const idleWorking = idle.find(s => s.key === 'working');
|
|
81
|
+
const idleIdle = idle.find(s => s.key === 'idle');
|
|
82
|
+
const idleOffline = idle.find(s => s.key === 'offline');
|
|
83
|
+
|
|
84
|
+
const hovWorking = hovered.find(s => s.key === 'working');
|
|
85
|
+
const hovIdle = hovered.find(s => s.key === 'idle');
|
|
86
|
+
const hovOffline = hovered.find(s => s.key === 'offline');
|
|
87
|
+
|
|
88
|
+
const settledWorking = settled.find(s => s.key === 'working');
|
|
89
|
+
|
|
90
|
+
const results = {
|
|
91
|
+
three_swatches: idle.length === 3,
|
|
92
|
+
has_working_swatch: !!idleWorking,
|
|
93
|
+
has_idle_swatch: !!idleIdle,
|
|
94
|
+
has_offline_swatch: !!idleOffline,
|
|
95
|
+
|
|
96
|
+
idle_working_state_idle: idleWorking?.state === 'idle',
|
|
97
|
+
idle_working_r_small: Math.abs((idleWorking?.cssR ?? idleWorking?.attrR ?? 0) - 5.5) < 0.4,
|
|
98
|
+
transition_includes_r: (idleWorking?.transition || '').includes('r') &&
|
|
99
|
+
(idleWorking?.transition || '').includes('150ms'),
|
|
100
|
+
|
|
101
|
+
hov_working_state_hover: hovWorking?.state === 'hover',
|
|
102
|
+
hov_working_r_bigger: (hovWorking?.cssR ?? hovWorking?.attrR ?? 0) > 6.5,
|
|
103
|
+
hov_working_r_about_7: Math.abs((hovWorking?.cssR ?? hovWorking?.attrR ?? 0) - 7) < 0.4,
|
|
104
|
+
hov_idle_stays_idle: hovIdle?.state === 'idle',
|
|
105
|
+
hov_idle_r_unchanged: Math.abs((hovIdle?.cssR ?? hovIdle?.attrR ?? 0) - 5.5) < 0.4,
|
|
106
|
+
hov_offline_stays_idle: hovOffline?.state === 'idle',
|
|
107
|
+
|
|
108
|
+
settled_state_idle: settledWorking?.state === 'idle',
|
|
109
|
+
settled_r_back_small: Math.abs((settledWorking?.cssR ?? settledWorking?.attrR ?? 0) - 5.5) < 0.4,
|
|
110
|
+
};
|
|
111
|
+
const ok = Object.values(results).every(Boolean);
|
|
112
|
+
console.log(`${ok ? '✅' : '❌'} legend swatch grow:`, JSON.stringify(results),
|
|
113
|
+
`\n idle:`, idle,
|
|
114
|
+
`\n hovered:`, hovered,
|
|
115
|
+
`\n settled:`, settled);
|
|
116
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/* Round 157 verification: minimap a11y completion.
|
|
2
|
+
*
|
|
3
|
+
* Pre-R157 the R30 minimap had role="img" (wrong — it's clickable)
|
|
4
|
+
* + aria-label but no tabIndex / onKeyDown. Mouse users could
|
|
5
|
+
* click to recenter; keyboard-only users couldn't reach or
|
|
6
|
+
* activate it.
|
|
7
|
+
*
|
|
8
|
+
* R157 closes the gap with the standard pattern (R116/R139/R140/
|
|
9
|
+
* R151/R152):
|
|
10
|
+
* role="button"
|
|
11
|
+
* tabIndex={0}
|
|
12
|
+
* aria-label="Topology minimap — click to recenter, Enter to reset view"
|
|
13
|
+
* onKeyDown(Enter/Space) → resetView()
|
|
14
|
+
* anet-topo-chip-focus className (R155 cyan outline)
|
|
15
|
+
*
|
|
16
|
+
* Test:
|
|
17
|
+
* 1. Force the canvas off-default via zoom (so minimap renders)
|
|
18
|
+
* 2. Verify role / tabIndex / aria-label / class on the minimap
|
|
19
|
+
* 3. Press Enter on focused minimap → view resets (zoom back to 100%)
|
|
20
|
+
*/
|
|
21
|
+
import { chromium } from 'playwright';
|
|
22
|
+
import { readFileSync } from 'node:fs';
|
|
23
|
+
|
|
24
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
25
|
+
const browser = await chromium.launch({ headless: true });
|
|
26
|
+
|
|
27
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
|
|
28
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
29
|
+
await ctx.addInitScript(() => {
|
|
30
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
31
|
+
});
|
|
32
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
33
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
34
|
+
const r = await route.fetch();
|
|
35
|
+
const b = await r.json();
|
|
36
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
37
|
+
const mk = (alias) => ({
|
|
38
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
39
|
+
network_id: nid, project_dir: null,
|
|
40
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
41
|
+
});
|
|
42
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta')] } });
|
|
43
|
+
});
|
|
44
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
45
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
46
|
+
|
|
47
|
+
const page = await ctx.newPage();
|
|
48
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
49
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 2, { timeout: 30000 });
|
|
50
|
+
await page.waitForTimeout(400);
|
|
51
|
+
|
|
52
|
+
// Initial: zoom is 100%, minimap not rendered (gated to non-default view)
|
|
53
|
+
const minimapBefore = await page.locator('[data-topo-minimap]').count();
|
|
54
|
+
|
|
55
|
+
// Press + (zoom in) twice to render the minimap
|
|
56
|
+
await page.keyboard.press('+');
|
|
57
|
+
await page.keyboard.press('+');
|
|
58
|
+
await page.waitForTimeout(250);
|
|
59
|
+
|
|
60
|
+
const probe = await page.evaluate(() => {
|
|
61
|
+
const el = document.querySelector('[data-topo-minimap]');
|
|
62
|
+
if (!el) return null;
|
|
63
|
+
return {
|
|
64
|
+
role: el.getAttribute('role'),
|
|
65
|
+
tabIndex: el.getAttribute('tabindex'),
|
|
66
|
+
ariaLabel: el.getAttribute('aria-label'),
|
|
67
|
+
title: el.getAttribute('title'),
|
|
68
|
+
className: el.getAttribute('class') || '',
|
|
69
|
+
zoomLevel: document.querySelector('[data-topo-chrome-zoom-level]')?.textContent?.trim(),
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Focus minimap + Enter → reset view (zoom back to 100%)
|
|
74
|
+
await page.locator('[data-topo-minimap]').focus();
|
|
75
|
+
await page.keyboard.press('Enter');
|
|
76
|
+
await page.waitForTimeout(250);
|
|
77
|
+
const afterEnter = await page.evaluate(() => ({
|
|
78
|
+
zoomLevel: document.querySelector('[data-topo-chrome-zoom-level]')?.textContent?.trim(),
|
|
79
|
+
minimapHidden: !document.querySelector('[data-topo-minimap]'),
|
|
80
|
+
}));
|
|
81
|
+
|
|
82
|
+
await browser.close();
|
|
83
|
+
|
|
84
|
+
const results = {
|
|
85
|
+
minimap_hiddenAtDefault: minimapBefore === 0,
|
|
86
|
+
minimap_renderedAfterZoom: probe !== null,
|
|
87
|
+
role_button: probe?.role === 'button',
|
|
88
|
+
tabIndex_0: probe?.tabIndex === '0',
|
|
89
|
+
ariaLabel_mentionsEnter: (probe?.ariaLabel || '').includes('Enter'),
|
|
90
|
+
title_mentionsEnter: (probe?.title || '').includes('Enter'),
|
|
91
|
+
hasFocusClass: (probe?.className || '').includes('anet-topo-chip-focus'),
|
|
92
|
+
zoomedBeforeReset: probe?.zoomLevel !== '100%',
|
|
93
|
+
enter_resetsZoom: afterEnter.zoomLevel === '100%',
|
|
94
|
+
enter_hidesMinimap: afterEnter.minimapHidden === true,
|
|
95
|
+
};
|
|
96
|
+
const ok = Object.values(results).every(Boolean);
|
|
97
|
+
console.log(`${ok ? '✅' : '❌'} minimap a11y:`, JSON.stringify(results),
|
|
98
|
+
`\n before count=${minimapBefore}`,
|
|
99
|
+
`\n probe=`, probe,
|
|
100
|
+
`\n afterEnter=`, afterEnter);
|
|
101
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/* Round 198 verification: minimap node dots get smooth opacity /
|
|
2
|
+
* fill / r transitions so a session flipping working→idle or going
|
|
3
|
+
* offline doesn't snap on the minimap mirror.
|
|
4
|
+
*
|
|
5
|
+
* Pre-R198 the canvas itself eased status changes (R167 stroke-width
|
|
6
|
+
* transition, R10 freshness alpha ramp, R3 fade-in chain), but the
|
|
7
|
+
* minimap below kept snap-cut. R198 brings the minimap dots in
|
|
8
|
+
* rhythm with the main canvas at 200ms.
|
|
9
|
+
*
|
|
10
|
+
* The minimap only mounts when isDefaultView=false (zoomed or panned),
|
|
11
|
+
* so the test must trigger a zoom-in first to populate it.
|
|
12
|
+
*
|
|
13
|
+
* Test:
|
|
14
|
+
* Mock 3 working + 1 offline session. Default view → no minimap.
|
|
15
|
+
* Click zoom-in → minimap mounts.
|
|
16
|
+
* Probe each minimap dot:
|
|
17
|
+
* 1. 4 dots present with data-topo-minimap-dot=<alias>
|
|
18
|
+
* 2. Online dots: r=1.7, opacity=0.9, data-...-online='true'
|
|
19
|
+
* 3. Offline dot: r=1.2, opacity=0.5, data-...-online='false'
|
|
20
|
+
* 4. Every dot's style.transition includes 'opacity 200ms',
|
|
21
|
+
* 'fill 200ms', and 'r 200ms'
|
|
22
|
+
*/
|
|
23
|
+
import { chromium } from 'playwright';
|
|
24
|
+
import { readFileSync } from 'node:fs';
|
|
25
|
+
|
|
26
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
27
|
+
const browser = await chromium.launch({ headless: true });
|
|
28
|
+
|
|
29
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
30
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
31
|
+
await ctx.addInitScript(() => {
|
|
32
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
36
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
37
|
+
const r = await route.fetch();
|
|
38
|
+
const b = await r.json();
|
|
39
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
40
|
+
const mk = (alias, status) => ({
|
|
41
|
+
alias, status, model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
42
|
+
network_id: nid, project_dir: null,
|
|
43
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
44
|
+
});
|
|
45
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
46
|
+
mk('alpha', 'working'),
|
|
47
|
+
mk('beta', 'working'),
|
|
48
|
+
mk('gamma', 'working'),
|
|
49
|
+
mk('delta', 'offline'),
|
|
50
|
+
] } });
|
|
51
|
+
});
|
|
52
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
53
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
54
|
+
|
|
55
|
+
const page = await ctx.newPage();
|
|
56
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
57
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
58
|
+
await page.waitForSelector('[data-topo-chrome-zoom-in]', { timeout: 10000 });
|
|
59
|
+
await page.waitForTimeout(300);
|
|
60
|
+
|
|
61
|
+
// Default view → minimap not mounted
|
|
62
|
+
const minimapBeforeZoom = await page.evaluate(() =>
|
|
63
|
+
document.querySelector('[data-topo-minimap]') !== null);
|
|
64
|
+
|
|
65
|
+
// Trigger zoom-in to mount the minimap
|
|
66
|
+
await page.click('[data-topo-chrome-zoom-in]');
|
|
67
|
+
await page.waitForTimeout(450); // past 280ms smoothView crossfade + margin
|
|
68
|
+
await page.waitForSelector('[data-topo-minimap-dot]', { timeout: 5000 });
|
|
69
|
+
await page.waitForTimeout(200);
|
|
70
|
+
|
|
71
|
+
const minimapAfterZoom = await page.evaluate(() =>
|
|
72
|
+
document.querySelector('[data-topo-minimap]') !== null);
|
|
73
|
+
|
|
74
|
+
const dots = await page.evaluate(() =>
|
|
75
|
+
[...document.querySelectorAll('[data-topo-minimap-dot]')].map(el => ({
|
|
76
|
+
alias: el.getAttribute('data-topo-minimap-dot'),
|
|
77
|
+
online: el.getAttribute('data-topo-minimap-dot-online'),
|
|
78
|
+
r: parseFloat(el.getAttribute('r') || ''),
|
|
79
|
+
opacity: parseFloat(el.getAttribute('opacity') || ''),
|
|
80
|
+
fill: el.getAttribute('fill'),
|
|
81
|
+
transition: el.style.transition,
|
|
82
|
+
})));
|
|
83
|
+
|
|
84
|
+
await browser.close();
|
|
85
|
+
|
|
86
|
+
const onlineDots = dots.filter(d => d.online === 'true');
|
|
87
|
+
const offlineDots = dots.filter(d => d.online === 'false');
|
|
88
|
+
|
|
89
|
+
const trans = (s) => s || '';
|
|
90
|
+
const hasOpacityT = (d) => /opacity\s+(0\.2s|200ms)/.test(trans(d.transition));
|
|
91
|
+
const hasFillT = (d) => /fill\s+(0\.2s|200ms)/.test(trans(d.transition));
|
|
92
|
+
const hasRT = (d) => /\br\s+(0\.2s|200ms)/.test(trans(d.transition));
|
|
93
|
+
|
|
94
|
+
const results = {
|
|
95
|
+
minimap_hidden_at_default: minimapBeforeZoom === false,
|
|
96
|
+
minimap_shown_after_zoom: minimapAfterZoom === true,
|
|
97
|
+
four_dots_present: dots.length === 4,
|
|
98
|
+
three_online: onlineDots.length === 3,
|
|
99
|
+
one_offline: offlineDots.length === 1,
|
|
100
|
+
online_r_1p7: onlineDots.every(d => Math.abs(d.r - 1.7) < 0.01),
|
|
101
|
+
online_opacity_0p9: onlineDots.every(d => Math.abs(d.opacity - 0.9) < 0.01),
|
|
102
|
+
offline_r_1p2: offlineDots.every(d => Math.abs(d.r - 1.2) < 0.01),
|
|
103
|
+
offline_opacity_0p5: offlineDots.every(d => Math.abs(d.opacity - 0.5) < 0.01),
|
|
104
|
+
all_have_opacity_transition: dots.every(hasOpacityT),
|
|
105
|
+
all_have_fill_transition: dots.every(hasFillT),
|
|
106
|
+
all_have_r_transition: dots.every(hasRT),
|
|
107
|
+
online_fill_truthy: onlineDots.every(d => d.fill && d.fill !== 'none'),
|
|
108
|
+
offline_fill_truthy: offlineDots.every(d => d.fill && d.fill !== 'none'),
|
|
109
|
+
};
|
|
110
|
+
const ok = Object.values(results).every(Boolean);
|
|
111
|
+
console.log(`${ok ? '✅' : '❌'} minimap dot transition:`, JSON.stringify(results),
|
|
112
|
+
`\n dots:`, dots);
|
|
113
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/* Round 30 verification: minimap.
|
|
2
|
+
* - not mounted at default view (1×, centered)
|
|
3
|
+
* - mounted when zoomed in (one node dot per session, viewport rect)
|
|
4
|
+
* - click recenters the canvas (view.x / view.y change) */
|
|
5
|
+
import { chromium } from 'playwright';
|
|
6
|
+
import { readFileSync } from 'node:fs';
|
|
7
|
+
|
|
8
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
9
|
+
const browser = await chromium.launch({ headless: true });
|
|
10
|
+
const ctx = await browser.newContext({ viewport: { width: 1280, height: 1600 } });
|
|
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.setItem('anet-topo-layout', 'grid');
|
|
16
|
+
localStorage.setItem('anet-topo-view', JSON.stringify({ zoom: 1, x: 0, y: 0 }));
|
|
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站', 'C站', 'D站', 'E站', 'F站'].flatMap(g =>
|
|
26
|
+
Array.from({ length: 3 }, (_, i) => ({
|
|
27
|
+
alias: `${g}${i + 1}`, status: 'idle', network_id: nid, project_dir: null,
|
|
28
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
29
|
+
})),
|
|
30
|
+
);
|
|
31
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
32
|
+
});
|
|
33
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
34
|
+
const page = await ctx.newPage();
|
|
35
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
36
|
+
await page.waitForSelector('svg[viewBox="0 0 1000 680"]', { timeout: 30000 });
|
|
37
|
+
await page.waitForTimeout(500);
|
|
38
|
+
|
|
39
|
+
const minimap = '[data-topo-minimap]';
|
|
40
|
+
const notMountedAtDefault = (await page.locator(minimap).count()) === 0;
|
|
41
|
+
|
|
42
|
+
// Zoom in via `+` twice → view changes → minimap mounts
|
|
43
|
+
await page.keyboard.press('=');
|
|
44
|
+
await page.keyboard.press('=');
|
|
45
|
+
await page.waitForTimeout(200);
|
|
46
|
+
const mountedAfterZoom = (await page.locator(minimap).count()) === 1;
|
|
47
|
+
const dotCount = await page.locator(`${minimap} svg circle`).count();
|
|
48
|
+
const hasViewportRect = await page.locator(`${minimap} svg rect`).count() > 0;
|
|
49
|
+
|
|
50
|
+
// Click on the minimap's far left → expect view.x to become more positive (canvas pans right)
|
|
51
|
+
const beforeView = await page.evaluate(() => JSON.parse(localStorage.getItem('anet-topo-view')));
|
|
52
|
+
const mmBox = await page.locator(minimap).boundingBox();
|
|
53
|
+
await page.mouse.click(mmBox.x + 10, mmBox.y + mmBox.height / 2);
|
|
54
|
+
await page.waitForTimeout(200);
|
|
55
|
+
const afterView = await page.evaluate(() => JSON.parse(localStorage.getItem('anet-topo-view')));
|
|
56
|
+
const clickRecenters = afterView.x !== beforeView.x || afterView.y !== beforeView.y;
|
|
57
|
+
|
|
58
|
+
await browser.close();
|
|
59
|
+
const results = {
|
|
60
|
+
notMountedAtDefault,
|
|
61
|
+
mountedAfterZoom,
|
|
62
|
+
oneDotPerSession: dotCount === 18, // 6 groups × 3 = 18 nodes
|
|
63
|
+
viewportRectPresent: hasViewportRect,
|
|
64
|
+
clickRecenters,
|
|
65
|
+
};
|
|
66
|
+
const ok = Object.values(results).every(Boolean);
|
|
67
|
+
console.log(`${ok ? '✅' : '❌'} minimap:`, JSON.stringify(results), `dots=${dotCount} view before=`, beforeView, `after=`, afterView);
|
|
68
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/* Round 199 verification: minimap viewport rect transitions x/y/w/h
|
|
2
|
+
* during R169 smoothView (discrete zoom +/− and reset). Closes the
|
|
3
|
+
* rhythm-mismatch R198 just fixed for minimap dots — now extended to
|
|
4
|
+
* the rectangle that frames them.
|
|
5
|
+
*
|
|
6
|
+
* Pre-R199 a click on zoom-in crossfaded the main canvas over R168's
|
|
7
|
+
* 280ms but the minimap viewport rect snap-cut to its new dimensions
|
|
8
|
+
* in one frame. R199 gates a `transition: x/y/w/h 280ms` on
|
|
9
|
+
* smoothView=true so discrete zooms ease the rect smoothly. Drag-pan
|
|
10
|
+
* and wheel-zoom (smoothView=false) keep snappy response — a
|
|
11
|
+
* persistent transition during drag would lag the cursor by 280ms.
|
|
12
|
+
*
|
|
13
|
+
* Test:
|
|
14
|
+
* 1. Open page with 4 sessions, zoom in once to mount minimap.
|
|
15
|
+
* 2. Idle (smoothView=false): rect has
|
|
16
|
+
* data-topo-minimap-viewport-smooth='false', style.transition='none'.
|
|
17
|
+
* 3. Click zoom-in again → smoothView arms to 'true' for ~350ms.
|
|
18
|
+
* 4. Within 100ms of click: probe rect →
|
|
19
|
+
* data-topo-minimap-viewport-smooth='true', style.transition
|
|
20
|
+
* includes 'x 280ms', 'y 280ms', 'width 280ms', 'height 280ms'.
|
|
21
|
+
* 5. Wait past 400ms → smoothView clears → back to 'false', no
|
|
22
|
+
* transition.
|
|
23
|
+
*/
|
|
24
|
+
import { chromium } from 'playwright';
|
|
25
|
+
import { readFileSync } from 'node:fs';
|
|
26
|
+
|
|
27
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
28
|
+
const browser = await chromium.launch({ headless: true });
|
|
29
|
+
|
|
30
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
31
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
32
|
+
await ctx.addInitScript(() => {
|
|
33
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
37
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
38
|
+
const r = await route.fetch();
|
|
39
|
+
const b = await r.json();
|
|
40
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
41
|
+
const mk = (alias) => ({
|
|
42
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
43
|
+
network_id: nid, project_dir: null,
|
|
44
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
45
|
+
});
|
|
46
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
47
|
+
mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
|
|
48
|
+
] } });
|
|
49
|
+
});
|
|
50
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
51
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
52
|
+
|
|
53
|
+
const page = await ctx.newPage();
|
|
54
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
55
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
56
|
+
await page.waitForSelector('[data-topo-chrome-zoom-in]', { timeout: 10000 });
|
|
57
|
+
await page.waitForTimeout(300);
|
|
58
|
+
|
|
59
|
+
// First zoom-in mounts the minimap. Wait past the 350ms smoothView
|
|
60
|
+
// timer so the minimap rect is at idle state (smooth='false') before
|
|
61
|
+
// we probe.
|
|
62
|
+
await page.click('[data-topo-chrome-zoom-in]');
|
|
63
|
+
await page.waitForTimeout(450);
|
|
64
|
+
await page.waitForSelector('[data-topo-minimap-viewport]', { timeout: 5000 });
|
|
65
|
+
|
|
66
|
+
const probe = async () => page.evaluate(() => {
|
|
67
|
+
const el = document.querySelector('[data-topo-minimap-viewport]');
|
|
68
|
+
if (!el) return null;
|
|
69
|
+
return {
|
|
70
|
+
smooth: el.getAttribute('data-topo-minimap-viewport-smooth'),
|
|
71
|
+
transition: el.style.transition,
|
|
72
|
+
x: parseFloat(el.getAttribute('x') || ''),
|
|
73
|
+
y: parseFloat(el.getAttribute('y') || ''),
|
|
74
|
+
w: parseFloat(el.getAttribute('width') || ''),
|
|
75
|
+
h: parseFloat(el.getAttribute('height') || ''),
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const idle = await probe();
|
|
80
|
+
|
|
81
|
+
// Click zoom-in once more — arms smoothView for ~350ms — and probe
|
|
82
|
+
// within the arming window.
|
|
83
|
+
await page.click('[data-topo-chrome-zoom-in]');
|
|
84
|
+
await page.waitForTimeout(80); // well inside the 350ms arming window
|
|
85
|
+
const armed = await probe();
|
|
86
|
+
|
|
87
|
+
await page.waitForTimeout(420); // past 350ms timer + transition
|
|
88
|
+
const settled = await probe();
|
|
89
|
+
|
|
90
|
+
await browser.close();
|
|
91
|
+
|
|
92
|
+
const hasAllAxes = (s) =>
|
|
93
|
+
/\bx\s+(?:280ms|0\.28s)/.test(s || '') &&
|
|
94
|
+
/\by\s+(?:280ms|0\.28s)/.test(s || '') &&
|
|
95
|
+
/\bwidth\s+(?:280ms|0\.28s)/.test(s || '') &&
|
|
96
|
+
/\bheight\s+(?:280ms|0\.28s)/.test(s || '');
|
|
97
|
+
|
|
98
|
+
const results = {
|
|
99
|
+
idle_smooth_false: idle?.smooth === 'false',
|
|
100
|
+
idle_transition_none: idle?.transition === 'none' || idle?.transition === '',
|
|
101
|
+
idle_has_dimensions: idle && Number.isFinite(idle.x) && Number.isFinite(idle.w) && idle.w > 0,
|
|
102
|
+
|
|
103
|
+
armed_smooth_true: armed?.smooth === 'true',
|
|
104
|
+
armed_transition_present: !!armed?.transition && armed.transition !== 'none',
|
|
105
|
+
armed_transition_has_all: hasAllAxes(armed?.transition),
|
|
106
|
+
|
|
107
|
+
settled_smooth_false: settled?.smooth === 'false',
|
|
108
|
+
settled_transition_none: settled?.transition === 'none' || settled?.transition === '',
|
|
109
|
+
|
|
110
|
+
// R199 actually changed dimensions between idle (zoom 120%) and
|
|
111
|
+
// settled (zoom 144% after second click), or x/y diverged.
|
|
112
|
+
zoom_changed_dimensions: idle && settled && (
|
|
113
|
+
Math.abs(idle.w - settled.w) > 1 ||
|
|
114
|
+
Math.abs(idle.h - settled.h) > 1 ||
|
|
115
|
+
Math.abs(idle.x - settled.x) > 1 ||
|
|
116
|
+
Math.abs(idle.y - settled.y) > 1
|
|
117
|
+
),
|
|
118
|
+
};
|
|
119
|
+
const ok = Object.values(results).every(Boolean);
|
|
120
|
+
console.log(`${ok ? '✅' : '❌'} minimap viewport smooth:`, JSON.stringify(results),
|
|
121
|
+
`\n idle:`, idle,
|
|
122
|
+
`\n armed:`, armed,
|
|
123
|
+
`\n settled:`, settled);
|
|
124
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/* Round 47 verification: mobile chip collapse.
|
|
2
|
+
* - At narrow viewport (375px), pressure / vendor-dist / active-links /
|
|
3
|
+
* freshness chips are display:none; only essential chips render.
|
|
4
|
+
* - At sm+ (≥640px), the full chip row is visible.
|
|
5
|
+
* - Essential chips (Ring/Grid toggle + working + online) render at both. */
|
|
6
|
+
import { chromium } from 'playwright';
|
|
7
|
+
import { readFileSync } from 'node:fs';
|
|
8
|
+
|
|
9
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
10
|
+
const browser = await chromium.launch({ headless: true });
|
|
11
|
+
|
|
12
|
+
async function probe(width) {
|
|
13
|
+
const ctx = await browser.newContext({ viewport: { width, height: 900 } });
|
|
14
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
15
|
+
await ctx.addInitScript(() => {
|
|
16
|
+
try {
|
|
17
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
18
|
+
localStorage.setItem('anet-topo-layout', 'grid');
|
|
19
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
20
|
+
} catch {}
|
|
21
|
+
});
|
|
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: 'wkr', status: 'working', network_id: nid, project_dir: null,
|
|
29
|
+
model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
30
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
31
|
+
{ alias: 'idl', status: 'idle', network_id: nid, project_dir: null,
|
|
32
|
+
model: 'gpt-5', runtime: 'codex-cli',
|
|
33
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
34
|
+
];
|
|
35
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
36
|
+
});
|
|
37
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
|
|
38
|
+
{ from_alias: 'wkr', to_alias: 'idl', content: 'm', created_at: new Date().toISOString() },
|
|
39
|
+
] } }));
|
|
40
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
41
|
+
const page = await ctx.newPage();
|
|
42
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
43
|
+
// The Overview page auto-hides the topology when window.innerWidth < 1024
|
|
44
|
+
// (the lg breakpoint). On narrow viewports the user clicks "Show
|
|
45
|
+
// Topology" to surface it; mimic that here so the chip-row tests reach
|
|
46
|
+
// the actual code path. Wait for SWR to deliver sessions first so the
|
|
47
|
+
// button renders before we look for it.
|
|
48
|
+
await page.waitForTimeout(1500);
|
|
49
|
+
const showBtn = page.locator('button', { hasText: /Show Topology/i });
|
|
50
|
+
if (await showBtn.count() > 0) await showBtn.first().click();
|
|
51
|
+
await page.waitForSelector('svg[viewBox="0 0 1000 680"]', { timeout: 30000 });
|
|
52
|
+
await page.waitForTimeout(600);
|
|
53
|
+
const visible = (sel) => page.evaluate(s => {
|
|
54
|
+
const el = document.querySelector(s);
|
|
55
|
+
if (!el) return null;
|
|
56
|
+
const r = el.getBoundingClientRect();
|
|
57
|
+
return r.width > 0 && r.height > 0;
|
|
58
|
+
}, sel);
|
|
59
|
+
const result = {
|
|
60
|
+
workingChipVisible: await page.evaluate(() => {
|
|
61
|
+
// "{N} working" chip — find the green-tinted one
|
|
62
|
+
const spans = [...document.querySelectorAll('span')];
|
|
63
|
+
const chip = spans.find(s => /^\d+ working$/.test((s.textContent || '').trim()));
|
|
64
|
+
if (!chip) return false;
|
|
65
|
+
const r = chip.getBoundingClientRect();
|
|
66
|
+
return r.width > 0 && r.height > 0;
|
|
67
|
+
}),
|
|
68
|
+
onlineChipVisible: await page.evaluate(() => {
|
|
69
|
+
const spans = [...document.querySelectorAll('span')];
|
|
70
|
+
const chip = spans.find(s => /^\d+ online$/.test((s.textContent || '').trim()));
|
|
71
|
+
if (!chip) return false;
|
|
72
|
+
const r = chip.getBoundingClientRect();
|
|
73
|
+
return r.width > 0 && r.height > 0;
|
|
74
|
+
}),
|
|
75
|
+
pressureVisible: await visible('[data-fleet-pressure]'),
|
|
76
|
+
activeLinksVisible: await page.evaluate(() => {
|
|
77
|
+
const spans = [...document.querySelectorAll('span')];
|
|
78
|
+
const chip = spans.find(s => /active link/.test(s.textContent || ''));
|
|
79
|
+
if (!chip) return false;
|
|
80
|
+
const r = chip.getBoundingClientRect();
|
|
81
|
+
return r.width > 0 && r.height > 0;
|
|
82
|
+
}),
|
|
83
|
+
freshnessVisible: await visible('[data-freshness-chip]'),
|
|
84
|
+
};
|
|
85
|
+
await ctx.close();
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const mobile = await probe(375);
|
|
90
|
+
const desktop = await probe(900);
|
|
91
|
+
await browser.close();
|
|
92
|
+
|
|
93
|
+
const results = {
|
|
94
|
+
mobileEssentialsVisible: mobile.workingChipVisible && mobile.onlineChipVisible,
|
|
95
|
+
mobilePressureHidden: mobile.pressureVisible === false,
|
|
96
|
+
mobileActiveLinksHidden: mobile.activeLinksVisible === false,
|
|
97
|
+
mobileFreshnessHidden: mobile.freshnessVisible === false,
|
|
98
|
+
desktopAllVisible: desktop.workingChipVisible && desktop.onlineChipVisible && desktop.pressureVisible && desktop.activeLinksVisible && desktop.freshnessVisible,
|
|
99
|
+
};
|
|
100
|
+
const ok = Object.values(results).every(Boolean);
|
|
101
|
+
console.log(`${ok ? '✅' : '❌'} mobile chips:`, JSON.stringify(results),
|
|
102
|
+
`\n mobile=`, mobile, `\n desktop=`, desktop);
|
|
103
|
+
process.exit(ok ? 0 : 1);
|