@sleep2agi/agent-network-dashboard 0.4.6-preview.11 → 0.4.6-preview.111
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 +69 -55
- 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/0052a9q2fvljq.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/0glmrdj9112tc.js +7 -0
- package/.next/static/chunks/0ofu4mc55-oar.js +4 -0
- package/.next/static/chunks/0rxrez_cjn.eh.js +1 -0
- package/.next/static/chunks/0tvn2l1pc.h65.js +1 -0
- package/.next/static/chunks/0x8x.29hkju5a.js +1 -0
- package/.next/static/chunks/115k17sotk9jg.css +2 -0
- package/.next/static/chunks/121d8yvfl8l_..js +7 -0
- package/.next/static/chunks/14ppi2e6ljtms.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/AppShell.tsx +2 -0
- package/app/components/ChatPopover.tsx +38 -2
- package/app/components/CommandPalette.tsx +67 -0
- package/app/components/HelpOverlay.tsx +71 -0
- package/app/components/ServersDrawer.tsx +206 -0
- package/app/components/TopoGraph.tsx +2032 -157
- package/app/components/utils.ts +8 -7
- package/app/globals.css +19 -1
- package/app/lib/time.ts +36 -0
- package/package.json +1 -1
- package/scripts/help-overlay-topo-test.mjs +44 -0
- package/scripts/servers-drawer-live-test.mjs +96 -0
- package/scripts/topo-active-filter-pill-test.mjs +101 -0
- package/scripts/topo-active-links-chip-hover-test.mjs +103 -0
- package/scripts/topo-active-links-chip-test.mjs +73 -0
- package/scripts/topo-arrival-ping-test.mjs +87 -0
- package/scripts/topo-arrow-tier-test.mjs +61 -0
- package/scripts/topo-auto-fit-test.mjs +73 -0
- package/scripts/topo-chat-focus-ring-test.mjs +77 -0
- package/scripts/topo-chip-pin-mirror-test.mjs +113 -0
- package/scripts/topo-click-ripple-test.mjs +66 -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-cwd-tooltip-test.mjs +64 -0
- package/scripts/topo-dblclick-reset-test.mjs +74 -0
- package/scripts/topo-dispatch-pulse-test.mjs +83 -0
- package/scripts/topo-edge-count-badge-test.mjs +84 -0
- package/scripts/topo-edge-endpoint-highlight-test.mjs +111 -0
- package/scripts/topo-edge-focus-test.mjs +113 -0
- package/scripts/topo-edge-hover-test.mjs +93 -0
- package/scripts/topo-edge-tooltip-test.mjs +70 -0
- package/scripts/topo-esc-clears-pin-test.mjs +98 -0
- package/scripts/topo-filter-pill-aliases-test.mjs +121 -0
- package/scripts/topo-filter-pill-body-click-test.mjs +112 -0
- package/scripts/topo-filter-pill-count-test.mjs +110 -0
- package/scripts/topo-filter-pill-mobile-test.mjs +90 -0
- package/scripts/topo-fit-key-test.mjs +106 -0
- package/scripts/topo-freshness-chip-test.mjs +74 -0
- package/scripts/topo-group-box-pinned-state-test.mjs +95 -0
- package/scripts/topo-group-count-test.mjs +68 -0
- package/scripts/topo-group-hover-test.mjs +82 -0
- package/scripts/topo-group-label-hover-test.mjs +119 -0
- package/scripts/topo-group-label-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-live-test.mjs +100 -0
- package/scripts/topo-groupbox-test.mjs +4 -2
- package/scripts/topo-help-overlay-r59-test.mjs +67 -0
- package/scripts/topo-help-overlay-r65-test.mjs +64 -0
- package/scripts/topo-help-overlay-r70-test.mjs +63 -0
- package/scripts/topo-help-overlay-r78-test.mjs +59 -0
- package/scripts/topo-help-overlay-r81-test.mjs +58 -0
- package/scripts/topo-help-overlay-r87-test.mjs +64 -0
- package/scripts/topo-help-overlay-r91-test.mjs +65 -0
- package/scripts/topo-hub-busyness-test.mjs +100 -0
- package/scripts/topo-hub-click-fit-test.mjs +137 -0
- package/scripts/topo-hub-title-test.mjs +93 -0
- package/scripts/topo-ingroup-edge-test.mjs +103 -0
- package/scripts/topo-label-lift-test.mjs +119 -0
- package/scripts/topo-label-scale-test.mjs +78 -0
- package/scripts/topo-lastseen-tooltip-test.mjs +59 -0
- package/scripts/topo-layout-key-test.mjs +91 -0
- package/scripts/topo-legend-count-test.mjs +88 -0
- package/scripts/topo-legend-hover-test.mjs +113 -0
- package/scripts/topo-legend-pin-test.mjs +128 -0
- package/scripts/topo-minimap-test.mjs +68 -0
- package/scripts/topo-mobile-chips-test.mjs +103 -0
- package/scripts/topo-node-hover-lift-test.mjs +85 -0
- package/scripts/topo-node-title-test.mjs +101 -0
- package/scripts/topo-offline-dim-test.mjs +70 -0
- package/scripts/topo-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-particle-stagger-test.mjs +82 -0
- package/scripts/topo-pin-persistence-test.mjs +105 -0
- package/scripts/topo-popover-meta-test.mjs +78 -0
- package/scripts/topo-pressure-bar-test.mjs +69 -0
- package/scripts/topo-pressure-hover-test.mjs +123 -0
- package/scripts/topo-pressure-pin-test.mjs +119 -0
- package/scripts/topo-pressure-seg-tooltip-test.mjs +98 -0
- package/scripts/topo-pulse-rate-test.mjs +71 -0
- package/scripts/topo-recent-panel-count-test.mjs +72 -0
- package/scripts/topo-recent-row-hover-test.mjs +122 -0
- package/scripts/topo-recent-row-tint-test.mjs +91 -0
- package/scripts/topo-recent-row-ts-test.mjs +92 -0
- package/scripts/topo-recent-signal-empty-test.mjs +63 -0
- package/scripts/topo-reduced-motion-test.mjs +77 -0
- package/scripts/topo-status-chip-hover-test.mjs +101 -0
- package/scripts/topo-tier-ring-occupancy-test.mjs +87 -0
- package/scripts/topo-tier-ring-test.mjs +69 -0
- package/scripts/topo-tier-ring-tint-test.mjs +110 -0
- package/scripts/topo-tier-stagger-test.mjs +89 -0
- package/scripts/topo-tz-ghost-test.mjs +69 -0
- package/scripts/topo-vendor-chip-hover-test.mjs +111 -0
- package/scripts/topo-vendor-letter-tooltip-test.mjs +92 -0
- package/scripts/topo-vendor-pill-test.mjs +125 -0
- package/scripts/topo-vendor-pin-test.mjs +125 -0
- package/scripts/topo-wheel-zoom-test.mjs +79 -0
- package/scripts/topo-zoom-keys-test.mjs +89 -0
- package/.next/static/chunks/0-7iowzu4.798.js +0 -1
- package/.next/static/chunks/02t5jr4shh.hy.js +0 -1
- package/.next/static/chunks/03cra9~ibhxtq.js +0 -1
- package/.next/static/chunks/0_qfg5s5uzs7..js +0 -7
- package/.next/static/chunks/0cj5qr_a8dzky.js +0 -7
- package/.next/static/chunks/0elalb9n_.~di.js +0 -1
- package/.next/static/chunks/0g-urk~xe4p41.js +0 -1
- 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/0r~2i_zubnm0u.js +0 -1
- package/.next/static/chunks/0~5-jv77qynyc.css +0 -2
- package/.next/static/chunks/10oi7l-tycgl9.js +0 -1
- package/.next/static/chunks/11uk1izuv3~0t.js +0 -1
- /package/.next/static/{-dh0O1dlTLFw1pzDolqQ3 → 93NJDvLYgCZAXqweNe_Ye}/_buildManifest.js +0 -0
- /package/.next/static/{-dh0O1dlTLFw1pzDolqQ3 → 93NJDvLYgCZAXqweNe_Ye}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{-dh0O1dlTLFw1pzDolqQ3 → 93NJDvLYgCZAXqweNe_Ye}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/* Round 78 verification: HelpOverlay surfaces the R73 (click pill body
|
|
2
|
+
* to clear) + R77 (hover N active links to brighten all flows) gestures.
|
|
3
|
+
* Both shipped as real user gestures but weren't in the discoverability
|
|
4
|
+
* surface.
|
|
5
|
+
*/
|
|
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
|
+
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
|
13
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
14
|
+
await ctx.addInitScript(() => {
|
|
15
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
16
|
+
});
|
|
17
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
18
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
19
|
+
const page = await ctx.newPage();
|
|
20
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
21
|
+
await page.waitForTimeout(800);
|
|
22
|
+
|
|
23
|
+
await page.keyboard.press('?');
|
|
24
|
+
await page.waitForSelector('[role="dialog"][aria-label="Keyboard shortcuts"]', { timeout: 5000 });
|
|
25
|
+
|
|
26
|
+
const items = await page.evaluate(() => {
|
|
27
|
+
const dialog = document.querySelector('[role="dialog"][aria-label="Keyboard shortcuts"]');
|
|
28
|
+
const groups = [...(dialog?.querySelectorAll('div') || [])];
|
|
29
|
+
let active = null;
|
|
30
|
+
for (const g of groups) {
|
|
31
|
+
if (g.textContent && /^Topology canvas$/i.test(g.textContent.trim())) {
|
|
32
|
+
active = g.parentElement;
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return active ? [...active.querySelectorAll('li')].map(li => (li.textContent || '').trim()) : [];
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
await browser.close();
|
|
40
|
+
|
|
41
|
+
const has = (s) => items.some(t => t.includes(s));
|
|
42
|
+
const results = {
|
|
43
|
+
r78_pillBodyClick: has('Clear filter — click anywhere on the pill body (R73)'),
|
|
44
|
+
r78_chipHover: has('Brighten all active flows — hover'),
|
|
45
|
+
// R74 and R70 priors should still appear (no regression)
|
|
46
|
+
r74_cmdkLayout: has('Toggle layout / Fit canvas via palette (R74)'),
|
|
47
|
+
r70_cmdkPin: has('(R69)'),
|
|
48
|
+
// R65 priors
|
|
49
|
+
r63_groupPin: has('Pin a team focus (R63)'),
|
|
50
|
+
r64_clearOne: has('Clear one filter (R64)'),
|
|
51
|
+
// R59 priors
|
|
52
|
+
r52_clickHub: has('Fit topology to canvas (R52)'),
|
|
53
|
+
r55_legendHover: has('Highlight nodes of a status (R55)'),
|
|
54
|
+
r56_signalHover: has('Highlight one flow edge (R56)'),
|
|
55
|
+
};
|
|
56
|
+
const ok = Object.values(results).every(Boolean);
|
|
57
|
+
console.log(`${ok ? '✅' : '❌'} help overlay R78:`, JSON.stringify(results),
|
|
58
|
+
`\n items=`, items);
|
|
59
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/* Round 81 verification: HelpOverlay surfaces R79 (status chip hover) +
|
|
2
|
+
* R80 (vendor chip letter hover). Both gestures landed without entries
|
|
3
|
+
* in the discoverability surface. Continues the R59/R62/R65/R70/R78
|
|
4
|
+
* docs-catch-up cadence.
|
|
5
|
+
*/
|
|
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
|
+
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
|
13
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
14
|
+
await ctx.addInitScript(() => {
|
|
15
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
16
|
+
});
|
|
17
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
18
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
19
|
+
const page = await ctx.newPage();
|
|
20
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
21
|
+
await page.waitForTimeout(800);
|
|
22
|
+
|
|
23
|
+
await page.keyboard.press('?');
|
|
24
|
+
await page.waitForSelector('[role="dialog"][aria-label="Keyboard shortcuts"]', { timeout: 5000 });
|
|
25
|
+
|
|
26
|
+
const items = await page.evaluate(() => {
|
|
27
|
+
const dialog = document.querySelector('[role="dialog"][aria-label="Keyboard shortcuts"]');
|
|
28
|
+
const groups = [...(dialog?.querySelectorAll('div') || [])];
|
|
29
|
+
let active = null;
|
|
30
|
+
for (const g of groups) {
|
|
31
|
+
if (g.textContent && /^Topology canvas$/i.test(g.textContent.trim())) {
|
|
32
|
+
active = g.parentElement;
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return active ? [...active.querySelectorAll('li')].map(li => (li.textContent || '').trim()) : [];
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
await browser.close();
|
|
40
|
+
|
|
41
|
+
const has = (s) => items.some(t => t.includes(s));
|
|
42
|
+
const results = {
|
|
43
|
+
r81_workingChip: has('Highlight working nodes — chip-row sibling of legend (R79)'),
|
|
44
|
+
r81_vendorChip: has('Highlight one vendor — hover A/O/书/? in the chip row (R80)'),
|
|
45
|
+
// R78 priors
|
|
46
|
+
r73_pillBodyClick: has('Clear filter — click anywhere on the pill body (R73)'),
|
|
47
|
+
r77_activeLinks: has('Brighten all active flows — hover'),
|
|
48
|
+
// R74 / R70 / R65 / R59 priors
|
|
49
|
+
r74_cmdkLayout: has('Toggle layout / Fit canvas via palette (R74)'),
|
|
50
|
+
r70_cmdkPin: has('(R69)'),
|
|
51
|
+
r63_groupPin: has('Pin a team focus (R63)'),
|
|
52
|
+
r60_pinChip: has('Pin a status filter (R60/R61)'),
|
|
53
|
+
r52_clickHub: has('Fit topology to canvas (R52)'),
|
|
54
|
+
};
|
|
55
|
+
const ok = Object.values(results).every(Boolean);
|
|
56
|
+
console.log(`${ok ? '✅' : '❌'} help overlay R81:`, JSON.stringify(results),
|
|
57
|
+
`\n items=`, items);
|
|
58
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/* Round 87 verification: HelpOverlay catches up on R83 (pressure-bar
|
|
2
|
+
* segment hover preview) + R86 (group-label hover preview). Continues
|
|
3
|
+
* the R59/R62/R65/R70/R78/R81 docs-catch-up cadence (~5 rounds).
|
|
4
|
+
*
|
|
5
|
+
* Stacks five generations of help-overlay assertions — any future
|
|
6
|
+
* regression that drops an entry will be caught by one of:
|
|
7
|
+
* R87 (this) → R81 → R78 → R74 → R70 → R65 → R59
|
|
8
|
+
*/
|
|
9
|
+
import { chromium } from 'playwright';
|
|
10
|
+
import { readFileSync } from 'node:fs';
|
|
11
|
+
|
|
12
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
13
|
+
const browser = await chromium.launch({ headless: true });
|
|
14
|
+
|
|
15
|
+
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
|
16
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
17
|
+
await ctx.addInitScript(() => {
|
|
18
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
19
|
+
});
|
|
20
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
21
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
22
|
+
const page = await ctx.newPage();
|
|
23
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
24
|
+
await page.waitForTimeout(800);
|
|
25
|
+
|
|
26
|
+
await page.keyboard.press('?');
|
|
27
|
+
await page.waitForSelector('[role="dialog"][aria-label="Keyboard shortcuts"]', { timeout: 5000 });
|
|
28
|
+
|
|
29
|
+
const items = await page.evaluate(() => {
|
|
30
|
+
const dialog = document.querySelector('[role="dialog"][aria-label="Keyboard shortcuts"]');
|
|
31
|
+
const groups = [...(dialog?.querySelectorAll('div') || [])];
|
|
32
|
+
let active = null;
|
|
33
|
+
for (const g of groups) {
|
|
34
|
+
if (g.textContent && /^Topology canvas$/i.test(g.textContent.trim())) {
|
|
35
|
+
active = g.parentElement;
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return active ? [...active.querySelectorAll('li')].map(li => (li.textContent || '').trim()) : [];
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
await browser.close();
|
|
43
|
+
|
|
44
|
+
const has = (s) => items.some(t => t.includes(s));
|
|
45
|
+
const results = {
|
|
46
|
+
r87_segHover: has('Preview a status filter — hover a pressure-bar segment (R83)'),
|
|
47
|
+
r87_groupLabelHover: has('Preview a team focus — hover a group label (R86)'),
|
|
48
|
+
// R81 priors
|
|
49
|
+
r81_workingChip: has('Highlight working nodes — chip-row sibling of legend (R79)'),
|
|
50
|
+
r81_vendorChip: has('Highlight one vendor — hover A/O/书/? in the chip row (R80)'),
|
|
51
|
+
// R78 priors
|
|
52
|
+
r78_pillBodyClick: has('Clear filter — click anywhere on the pill body (R73)'),
|
|
53
|
+
r78_activeLinks: has('Brighten all active flows — hover'),
|
|
54
|
+
// R74 / R70 / R65 / R59 priors — five generations of docs catch-up
|
|
55
|
+
r74_cmdkLayout: has('Toggle layout / Fit canvas via palette (R74)'),
|
|
56
|
+
r70_cmdkPin: has('(R69)'),
|
|
57
|
+
r65_groupClick: has('Pin a team focus (R63)'),
|
|
58
|
+
r62_pinChip: has('Pin a status filter (R60/R61)'),
|
|
59
|
+
r59_clickHub: has('Fit topology to canvas (R52)'),
|
|
60
|
+
};
|
|
61
|
+
const ok = Object.values(results).every(Boolean);
|
|
62
|
+
console.log(`${ok ? '✅' : '❌'} help overlay R87:`, JSON.stringify(results),
|
|
63
|
+
`\n items=`, items);
|
|
64
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/* Round 91 verification: HelpOverlay catches up on R88 vendor click-
|
|
2
|
+
* to-pin and R90 granular Cmd+K vendor-clear. Continues the
|
|
3
|
+
* R59/R62/R65/R70/R78/R81/R87 docs-catch-up cadence.
|
|
4
|
+
*
|
|
5
|
+
* Seven generations of help-overlay assertions stacked here — any
|
|
6
|
+
* future regression that drops an entry will be caught by one of
|
|
7
|
+
* R91 → R87 → R81 → R78 → R74 → R70 → R65 → R59.
|
|
8
|
+
*/
|
|
9
|
+
import { chromium } from 'playwright';
|
|
10
|
+
import { readFileSync } from 'node:fs';
|
|
11
|
+
|
|
12
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
13
|
+
const browser = await chromium.launch({ headless: true });
|
|
14
|
+
|
|
15
|
+
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
|
16
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
17
|
+
await ctx.addInitScript(() => {
|
|
18
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
19
|
+
});
|
|
20
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
21
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
22
|
+
const page = await ctx.newPage();
|
|
23
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
24
|
+
await page.waitForTimeout(800);
|
|
25
|
+
|
|
26
|
+
await page.keyboard.press('?');
|
|
27
|
+
await page.waitForSelector('[role="dialog"][aria-label="Keyboard shortcuts"]', { timeout: 5000 });
|
|
28
|
+
|
|
29
|
+
const items = await page.evaluate(() => {
|
|
30
|
+
const dialog = document.querySelector('[role="dialog"][aria-label="Keyboard shortcuts"]');
|
|
31
|
+
const groups = [...(dialog?.querySelectorAll('div') || [])];
|
|
32
|
+
let active = null;
|
|
33
|
+
for (const g of groups) {
|
|
34
|
+
if (g.textContent && /^Topology canvas$/i.test(g.textContent.trim())) {
|
|
35
|
+
active = g.parentElement;
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return active ? [...active.querySelectorAll('li')].map(li => (li.textContent || '').trim()) : [];
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
await browser.close();
|
|
43
|
+
|
|
44
|
+
const has = (s) => items.some(t => t.includes(s));
|
|
45
|
+
const results = {
|
|
46
|
+
r91_vendorPin: has('Pin a vendor filter — click A/O/书/? in the chip row (R88)'),
|
|
47
|
+
r91_cmdkVendorClear: has('Clear vendor pin only via palette → "Clear topology vendor filter" (R90)'),
|
|
48
|
+
// R87 priors (hover preview pair)
|
|
49
|
+
r87_segHover: has('Preview a status filter — hover a pressure-bar segment (R83)'),
|
|
50
|
+
r87_groupLabelHover: has('Preview a team focus — hover a group label (R86)'),
|
|
51
|
+
// R81 priors (chip-row hover pair)
|
|
52
|
+
r81_workingChip: has('Highlight working nodes — chip-row sibling of legend (R79)'),
|
|
53
|
+
r81_vendorChip: has('Highlight one vendor — hover A/O/书/? in the chip row (R80)'),
|
|
54
|
+
// R78 / R74 / R70 / R65 / R59 priors — seven generations
|
|
55
|
+
r78_pillBodyClick: has('Clear filter — click anywhere on the pill body (R73)'),
|
|
56
|
+
r74_cmdkLayout: has('Toggle layout / Fit canvas via palette (R74)'),
|
|
57
|
+
r70_cmdkPin: has('(R69)'),
|
|
58
|
+
r65_groupClick: has('Pin a team focus (R63)'),
|
|
59
|
+
r62_pinChip: has('Pin a status filter (R60/R61)'),
|
|
60
|
+
r59_clickHub: has('Fit topology to canvas (R52)'),
|
|
61
|
+
};
|
|
62
|
+
const ok = Object.values(results).every(Boolean);
|
|
63
|
+
console.log(`${ok ? '✅' : '❌'} help overlay R91:`, JSON.stringify(results),
|
|
64
|
+
`\n items=`, items);
|
|
65
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/* Round 84 verification: hub-halo breath amplitude + tempo reflect
|
|
2
|
+
* `workingCount`. Buckets: 0 / 1-2 / 3-5 / 6+. An idle fleet keeps
|
|
3
|
+
* the original 0.32→0.52 (light) / 0.08→0.16 (dark) cycle over 4s;
|
|
4
|
+
* a busy fleet pulses faster with a higher peak. Pure visual breath
|
|
5
|
+
* — geometry unchanged, overlap test untouched.
|
|
6
|
+
*
|
|
7
|
+
* Drive workingCount via mocked /api/hub/status sessions, then read
|
|
8
|
+
* the SMIL `<animate>` attributes off the hub halo. The bucket gets
|
|
9
|
+
* surfaced as `data-hub-busyness=0..3` for stable assertion without
|
|
10
|
+
* parsing values strings.
|
|
11
|
+
*/
|
|
12
|
+
import { chromium } from 'playwright';
|
|
13
|
+
import { readFileSync } from 'node:fs';
|
|
14
|
+
|
|
15
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
16
|
+
const browser = await chromium.launch({ headless: true });
|
|
17
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
18
|
+
|
|
19
|
+
const probe = async (workers) => {
|
|
20
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1200 } });
|
|
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
|
+
} catch {}
|
|
28
|
+
});
|
|
29
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
30
|
+
const r = await route.fetch();
|
|
31
|
+
const b = await r.json();
|
|
32
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
33
|
+
const sessions = [];
|
|
34
|
+
for (let i = 0; i < workers; i++) {
|
|
35
|
+
sessions.push({ alias: `wkr${i}`, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
36
|
+
network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh });
|
|
37
|
+
}
|
|
38
|
+
sessions.push({ alias: 'idl', status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
39
|
+
network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh });
|
|
40
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
41
|
+
});
|
|
42
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
43
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
44
|
+
const page = await ctx.newPage();
|
|
45
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
46
|
+
await page.waitForFunction((n) => document.querySelectorAll('g[data-node]').length === n,
|
|
47
|
+
workers + 1, { timeout: 30000 });
|
|
48
|
+
await page.waitForTimeout(400);
|
|
49
|
+
|
|
50
|
+
const data = await page.evaluate(() => {
|
|
51
|
+
const halo = document.querySelector('[data-hub-busyness]');
|
|
52
|
+
const ani = halo?.querySelector('animate');
|
|
53
|
+
return {
|
|
54
|
+
busy: halo?.getAttribute('data-hub-busyness'),
|
|
55
|
+
values: ani?.getAttribute('values') || null,
|
|
56
|
+
dur: ani?.getAttribute('dur') || null,
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
await ctx.close();
|
|
60
|
+
return data;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Helper: parse "trough;peak;trough" into [trough, peak].
|
|
64
|
+
const parseValues = (v) => v ? v.split(';').map(Number) : [];
|
|
65
|
+
const peakOf = (v) => Math.max(...parseValues(v));
|
|
66
|
+
const durOf = (s) => s ? parseFloat(s.replace('s', '')) : 0;
|
|
67
|
+
|
|
68
|
+
const b0 = await probe(0); // idle only
|
|
69
|
+
const b1 = await probe(1); // 1 worker → bucket 1
|
|
70
|
+
const b2 = await probe(4); // 4 workers → bucket 2
|
|
71
|
+
const b3 = await probe(7); // 7 workers → bucket 3
|
|
72
|
+
await browser.close();
|
|
73
|
+
|
|
74
|
+
const results = {
|
|
75
|
+
b0_bucket: b0.busy === '0',
|
|
76
|
+
b1_bucket: b1.busy === '1',
|
|
77
|
+
b2_bucket: b2.busy === '2',
|
|
78
|
+
b3_bucket: b3.busy === '3',
|
|
79
|
+
// Peak climbs with bucket — dark theme values.
|
|
80
|
+
peak_monotonic: peakOf(b0.values) < peakOf(b1.values) &&
|
|
81
|
+
peakOf(b1.values) < peakOf(b2.values) &&
|
|
82
|
+
peakOf(b2.values) < peakOf(b3.values),
|
|
83
|
+
// Duration shortens with bucket.
|
|
84
|
+
dur_monotonic: durOf(b0.dur) > durOf(b1.dur) &&
|
|
85
|
+
durOf(b1.dur) > durOf(b2.dur) &&
|
|
86
|
+
durOf(b2.dur) > durOf(b3.dur),
|
|
87
|
+
// Idle baseline preserved.
|
|
88
|
+
b0_idleDur_4s: durOf(b0.dur) === 4,
|
|
89
|
+
// Busy cap respected.
|
|
90
|
+
b3_capped_24s: durOf(b3.dur) === 2.4,
|
|
91
|
+
// All have the same trough (preserving the rest baseline).
|
|
92
|
+
trough_constant: parseValues(b0.values)[0] === parseValues(b3.values)[0],
|
|
93
|
+
};
|
|
94
|
+
const ok = Object.values(results).every(Boolean);
|
|
95
|
+
console.log(`${ok ? '✅' : '❌'} hub busyness:`, JSON.stringify(results),
|
|
96
|
+
`\n b0=`, b0,
|
|
97
|
+
`\n b1=`, b1,
|
|
98
|
+
`\n b2=`, b2,
|
|
99
|
+
`\n b3=`, b3);
|
|
100
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/* Round 52 verification: clicking the central hub triggers fitView() and
|
|
2
|
+
* shows a click-ripple. The hub was previously decoration; now it's the
|
|
3
|
+
* "re-center" affordance for users who don't know the `f` shortcut.
|
|
4
|
+
*
|
|
5
|
+
* Steps:
|
|
6
|
+
* - Zoom + pan the topology to a non-default view (Cmd+= twice + drag).
|
|
7
|
+
* Confirm the zoom group's transform shifted off identity.
|
|
8
|
+
* - Click the hub. Assert:
|
|
9
|
+
* 1. the zoom group's transform returns to a fitted state (zoom and
|
|
10
|
+
* translate change), i.e. the view changed back toward identity-
|
|
11
|
+
* or-fit;
|
|
12
|
+
* 2. a transient ripple <circle> shows up at the hub center, then
|
|
13
|
+
* vanishes within ~800ms.
|
|
14
|
+
* - Confirm the hub's <title> mentions "click to fit view".
|
|
15
|
+
*/
|
|
16
|
+
import { chromium } from 'playwright';
|
|
17
|
+
import { readFileSync } from 'node:fs';
|
|
18
|
+
|
|
19
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
20
|
+
const browser = await chromium.launch({ headless: true });
|
|
21
|
+
|
|
22
|
+
const ctx = await browser.newContext({ viewport: { width: 1280, height: 1500 } });
|
|
23
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
24
|
+
await ctx.addInitScript(() => {
|
|
25
|
+
try {
|
|
26
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
27
|
+
localStorage.setItem('anet-topo-layout', 'ring');
|
|
28
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
29
|
+
} 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 sessions = ['alpha', 'beta', 'gamma'].map(a => ({
|
|
38
|
+
alias: a, status: 'idle', 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 } });
|
|
42
|
+
});
|
|
43
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
44
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
45
|
+
const page = await ctx.newPage();
|
|
46
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
47
|
+
await page.waitForSelector('g[data-topo-hub]', { timeout: 30000 });
|
|
48
|
+
await page.waitForTimeout(500);
|
|
49
|
+
|
|
50
|
+
// Read the hub <title> to verify the hint text.
|
|
51
|
+
const hubTitle = await page.evaluate(() => {
|
|
52
|
+
return document.querySelector('g[data-topo-hub] title')?.textContent || '';
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Find the zoom/pan wrapper <g> (the one whose transform attribute holds
|
|
56
|
+
// the scale + translate string). The TopoGraph wraps all canvas content
|
|
57
|
+
// in a <g> with transform="translate(x, y) scale(zoom)" at the panel root.
|
|
58
|
+
// Grab the first child <g> under the SVG that carries a `transform` with
|
|
59
|
+
// a `scale(` token — that's our zoom group.
|
|
60
|
+
const readTransform = () => page.evaluate(() => {
|
|
61
|
+
const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
|
|
62
|
+
for (const g of svg.querySelectorAll(':scope > g')) {
|
|
63
|
+
const t = g.getAttribute('transform') || '';
|
|
64
|
+
if (t.includes('scale(')) return t;
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const baseTransform = await readTransform();
|
|
70
|
+
|
|
71
|
+
// Nudge zoom by simulating Ctrl+= twice via keyboard shortcut. The
|
|
72
|
+
// keymap maps '+' to zoomBy; press 'Equal' with the right modifier so
|
|
73
|
+
// browsers see '+'.
|
|
74
|
+
await page.keyboard.press('+');
|
|
75
|
+
await page.keyboard.press('+');
|
|
76
|
+
await page.keyboard.press('+');
|
|
77
|
+
await page.waitForTimeout(150);
|
|
78
|
+
const zoomedTransform = await readTransform();
|
|
79
|
+
|
|
80
|
+
// Confirm the zoom transform actually changed.
|
|
81
|
+
const zoomChanged = baseTransform !== zoomedTransform;
|
|
82
|
+
|
|
83
|
+
// Click the hub center. Locator-based click hits the union bbox edge and
|
|
84
|
+
// gets intercepted by the SVG panel rect (which must keep pointer events
|
|
85
|
+
// for canvas pan). Sample the inner core circle (r=10 at viewBox 500,330)
|
|
86
|
+
// via getScreenCTM and click that on-screen point directly.
|
|
87
|
+
const hubCenter = await page.evaluate(() => {
|
|
88
|
+
const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
|
|
89
|
+
const core = [...svg.querySelectorAll('g[data-topo-hub] circle')].find(
|
|
90
|
+
c => c.getAttribute('r') === '10'
|
|
91
|
+
);
|
|
92
|
+
if (!core) return null;
|
|
93
|
+
const ctm = core.getScreenCTM();
|
|
94
|
+
const cx = +core.getAttribute('cx');
|
|
95
|
+
const cy = +core.getAttribute('cy');
|
|
96
|
+
return { x: cx * ctm.a + cy * ctm.c + ctm.e, y: cx * ctm.b + cy * ctm.d + ctm.f };
|
|
97
|
+
});
|
|
98
|
+
if (!hubCenter) { console.log('❌ hub core circle not found'); process.exit(1); }
|
|
99
|
+
await page.mouse.click(hubCenter.x, hubCenter.y);
|
|
100
|
+
await page.waitForTimeout(150);
|
|
101
|
+
const afterClickTransform = await readTransform();
|
|
102
|
+
const ripplePresent = await page.evaluate(() => {
|
|
103
|
+
const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
|
|
104
|
+
// The click ripple is a <circle> with stroke-width="2" + an <animate>.
|
|
105
|
+
const ripples = [...svg.querySelectorAll('circle')].filter(c => {
|
|
106
|
+
const sw = c.getAttribute('stroke-width');
|
|
107
|
+
return sw === '2' && c.querySelector('animate[attributeName="r"]');
|
|
108
|
+
});
|
|
109
|
+
return ripples.length > 0;
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
await page.waitForTimeout(800);
|
|
113
|
+
const rippleGone = await page.evaluate(() => {
|
|
114
|
+
const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
|
|
115
|
+
const ripples = [...svg.querySelectorAll('circle')].filter(c => {
|
|
116
|
+
const sw = c.getAttribute('stroke-width');
|
|
117
|
+
return sw === '2' && c.querySelector('animate[attributeName="r"]');
|
|
118
|
+
});
|
|
119
|
+
return ripples.length === 0;
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
await browser.close();
|
|
123
|
+
|
|
124
|
+
const results = {
|
|
125
|
+
hubTitleHasFitHint: /click to fit view/i.test(hubTitle),
|
|
126
|
+
zoomShifted: zoomChanged,
|
|
127
|
+
hubClickRestoredView: afterClickTransform !== zoomedTransform,
|
|
128
|
+
ripplePresentDuringClick: ripplePresent,
|
|
129
|
+
rippleClearsAfter600ms: rippleGone,
|
|
130
|
+
};
|
|
131
|
+
const ok = Object.values(results).every(Boolean);
|
|
132
|
+
console.log(`${ok ? '✅' : '❌'} hub click fit:`, JSON.stringify(results),
|
|
133
|
+
`\n hubTitle=${JSON.stringify(hubTitle)}`,
|
|
134
|
+
`\n base =${baseTransform}`,
|
|
135
|
+
`\n zoomed =${zoomedTransform}`,
|
|
136
|
+
`\n fitBack =${afterClickTransform}`);
|
|
137
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/* Round 43 verification: central hub carries a `<title>` summary in ring
|
|
2
|
+
* layout. Assertions:
|
|
3
|
+
* - hub renders in ring layout (data-topo-hub group present)
|
|
4
|
+
* - <title> reads "Network hub · N sessions · …" with the right counts
|
|
5
|
+
* - zero-count clauses (online/working/links) drop cleanly
|
|
6
|
+
* - grid layout doesn't render the hub at all */
|
|
7
|
+
import { chromium } from 'playwright';
|
|
8
|
+
import { readFileSync } from 'node:fs';
|
|
9
|
+
|
|
10
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
11
|
+
const browser = await chromium.launch({ headless: true });
|
|
12
|
+
|
|
13
|
+
async function probe({ layout, sessionsSpec, msgs = [] }) {
|
|
14
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1000 } });
|
|
15
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
16
|
+
await ctx.addInitScript((lay) => {
|
|
17
|
+
try {
|
|
18
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
19
|
+
localStorage.setItem('anet-topo-layout', lay);
|
|
20
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
21
|
+
} catch {}
|
|
22
|
+
}, layout);
|
|
23
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
24
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
25
|
+
const r = await route.fetch();
|
|
26
|
+
const b = await r.json();
|
|
27
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
28
|
+
const sessions = sessionsSpec.map(s => ({
|
|
29
|
+
alias: s.alias, status: s.status, 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 } });
|
|
33
|
+
});
|
|
34
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
|
|
35
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
36
|
+
const page = await ctx.newPage();
|
|
37
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
38
|
+
await page.waitForSelector('svg[viewBox="0 0 1000 680"]', { timeout: 30000 });
|
|
39
|
+
await page.waitForTimeout(500);
|
|
40
|
+
const data = await page.evaluate(() => {
|
|
41
|
+
const hub = document.querySelector('g[data-topo-hub]');
|
|
42
|
+
const title = hub?.querySelector('title');
|
|
43
|
+
return { hubPresent: !!hub, titleText: title?.textContent || null };
|
|
44
|
+
});
|
|
45
|
+
await ctx.close();
|
|
46
|
+
return data;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Ring + mixed-status fleet → full summary line
|
|
50
|
+
const ringFull = await probe({
|
|
51
|
+
layout: 'ring',
|
|
52
|
+
sessionsSpec: [
|
|
53
|
+
{ alias: 'a', status: 'working' },
|
|
54
|
+
{ alias: 'b', status: 'idle' },
|
|
55
|
+
{ alias: 'c', status: 'idle' },
|
|
56
|
+
{ alias: 'd', status: 'offline' },
|
|
57
|
+
],
|
|
58
|
+
msgs: [
|
|
59
|
+
{ from_alias: 'a', to_alias: 'b', content: 'm', created_at: new Date().toISOString() },
|
|
60
|
+
],
|
|
61
|
+
});
|
|
62
|
+
// Ring + all-offline → online/working/links clauses drop
|
|
63
|
+
const ringOfflineOnly = await probe({
|
|
64
|
+
layout: 'ring',
|
|
65
|
+
sessionsSpec: [
|
|
66
|
+
{ alias: 'x', status: 'offline' },
|
|
67
|
+
{ alias: 'y', status: 'offline' },
|
|
68
|
+
],
|
|
69
|
+
});
|
|
70
|
+
// Grid → no hub at all
|
|
71
|
+
const grid = await probe({
|
|
72
|
+
layout: 'grid',
|
|
73
|
+
sessionsSpec: [{ alias: 'a', status: 'idle' }, { alias: 'b', status: 'idle' }],
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
await browser.close();
|
|
77
|
+
// R52 appended "\nclick to fit view" as a second line to the hub
|
|
78
|
+
// <title> so the click-to-fit affordance is discoverable on hover.
|
|
79
|
+
// Compare on the first line only; assert the hint is present too.
|
|
80
|
+
const firstLine = (s) => (s || '').split('\n')[0];
|
|
81
|
+
const results = {
|
|
82
|
+
ringHubPresent: ringFull.hubPresent === true,
|
|
83
|
+
ringFullSummary: firstLine(ringFull.titleText) === 'Network hub · 4 sessions · 3 online · 1 working · 1 active link',
|
|
84
|
+
ringOfflineSummary: ringOfflineOnly.hubPresent === true && firstLine(ringOfflineOnly.titleText) === 'Network hub · 2 sessions',
|
|
85
|
+
ringHintsAtFit: /click to fit view/i.test(ringFull.titleText || ''),
|
|
86
|
+
gridHasNoHub: grid.hubPresent === false,
|
|
87
|
+
};
|
|
88
|
+
const ok = Object.values(results).every(Boolean);
|
|
89
|
+
console.log(`${ok ? '✅' : '❌'} hub title:`, JSON.stringify(results),
|
|
90
|
+
`\n ringFull=${JSON.stringify(ringFull.titleText)}`,
|
|
91
|
+
`\n ringOffline=${JSON.stringify(ringOfflineOnly.titleText)}`,
|
|
92
|
+
`\n gridHub=${grid.hubPresent}`);
|
|
93
|
+
process.exit(ok ? 0 : 1);
|