@sleep2agi/agent-network-dashboard 0.4.6 → 0.5.1-preview.0
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 +2 -0
- package/.next/build-manifest.json +3 -3
- package/.next/diagnostics/route-bundle-stats.json +31 -31
- package/.next/fallback-build-manifest.json +3 -3
- package/.next/prerender-manifest.json +3 -3
- package/.next/routes-manifest.json +16 -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/server/[hostname]/agents/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/hub/server/[hostname]/agents/route/build-manifest.json +9 -0
- package/.next/server/app/api/hub/server/[hostname]/agents/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/hub/server/[hostname]/agents/route.js +7 -0
- package/.next/server/app/api/hub/server/[hostname]/agents/route.js.map +5 -0
- package/.next/server/app/api/hub/server/[hostname]/agents/route.js.nft.json +1 -0
- package/.next/server/app/api/hub/server/[hostname]/agents/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/hub/server/[hostname]/health/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/hub/server/[hostname]/health/route/build-manifest.json +9 -0
- package/.next/server/app/api/hub/server/[hostname]/health/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/hub/server/[hostname]/health/route.js +7 -0
- package/.next/server/app/api/hub/server/[hostname]/health/route.js.map +5 -0
- package/.next/server/app/api/hub/server/[hostname]/health/route.js.nft.json +1 -0
- package/.next/server/app/api/hub/server/[hostname]/health/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 +2 -0
- package/.next/server/chunks/0dk5_next-internal_server_app_api_hub_server_[hostname]_agents_route_actions_0vr92s4.js +3 -0
- package/.next/server/chunks/0dk5_next-internal_server_app_api_hub_server_[hostname]_agents_route_actions_0vr92s4.js.map +1 -0
- package/.next/server/chunks/0dk5_next-internal_server_app_api_hub_server_[hostname]_health_route_actions_0vr-bv5.js +3 -0
- package/.next/server/chunks/0dk5_next-internal_server_app_api_hub_server_[hostname]_health_route_actions_0vr-bv5.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__05nyivw._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__05nyivw._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0floe3h._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0floe3h._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0tx8s8i._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__0tx8s8i._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +4 -2
- 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_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_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/03v-0n3d~t1sm.js +4 -0
- package/.next/static/chunks/0aauz~36q5n2a.css +2 -0
- package/.next/static/chunks/0l_~q07bhpkcx.js +7 -0
- package/.next/static/chunks/{0hyl7mdapsz38.js → 0vt5wron8jig5.js} +1 -1
- package/.next/static/chunks/{16yfxp9ak2gdm.js → 180biiw78-hb_.js} +1 -1
- package/.next/trace +2 -2
- package/.next/trace-build +1 -1
- package/.next/types/routes.d.ts +3 -1
- package/.next/types/validator.ts +18 -0
- package/app/api/hub/server/[hostname]/agents/route.ts +62 -0
- package/app/api/hub/server/[hostname]/health/route.ts +58 -0
- package/app/api/hub/servers/route.ts +88 -1
- package/app/components/CommandPalette.tsx +107 -0
- package/app/components/HelpOverlay.tsx +109 -1
- package/app/components/ServersDrawer.tsx +181 -5
- package/app/components/TopoGraph.tsx +6420 -286
- package/app/globals.css +110 -5
- package/app/lib/vendorIdentity.ts +14 -3
- package/docs/rfc/v0.10.0-hero3-dashboard-upgrade.md +230 -0
- package/package.json +2 -2
- package/public/sleep2agi-logo.svg +11 -0
- package/public/vendors/claude.svg +11 -0
- package/public/vendors/minimax.svg +11 -0
- package/public/vendors/openai.svg +12 -0
- package/screenshots/r293-p0/after-full.png +0 -0
- package/screenshots/r293-p0/after-topo.png +0 -0
- package/screenshots/r293-p0/baseline-full.png +0 -0
- package/screenshots/r293-p0/baseline-topo.png +0 -0
- package/screenshots/r293-p0/logo-integrated-full.png +0 -0
- package/screenshots/r293-p0/logo-integrated-topo.png +0 -0
- package/screenshots/r293-p0/servers-drawer-v0.10.0-full.png +0 -0
- package/screenshots/r293-p0/servers-drawer-v0.10.0.png +0 -0
- package/screenshots/r293-p0/wave1-after-full.png +0 -0
- package/screenshots/r293-p0/wave1-after-topo.png +0 -0
- package/scripts/p0-servers-drawer-screenshot.mjs +99 -0
- package/scripts/p0-topo-screenshot.mjs +82 -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-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-active-links-unit-test.mjs +79 -0
- package/scripts/topo-active-pulse-ease-test.mjs +120 -0
- package/scripts/topo-brand-logo-transition-test.mjs +94 -0
- package/scripts/topo-brand-watermark-test.mjs +97 -0
- package/scripts/topo-canvas-aria-test.mjs +108 -0
- package/scripts/topo-canvas-brand-fade-test.mjs +87 -0
- package/scripts/topo-chat-target-breath-test.mjs +84 -0
- package/scripts/topo-chat-target-ring-ease-test.mjs +82 -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-chip-row-gap-test.mjs +90 -0
- package/scripts/topo-chip-row-semantic-gap-test.mjs +97 -0
- package/scripts/topo-chip-row-tabular-test.mjs +111 -0
- package/scripts/topo-chip-unit-demotion-test.mjs +93 -0
- package/scripts/topo-chiprow-data-weight-test.mjs +94 -0
- package/scripts/topo-chrome-a11y-test.mjs +107 -0
- package/scripts/topo-chrome-active-weight-test.mjs +104 -0
- package/scripts/topo-chrome-focus-ring-unify-test.mjs +100 -0
- package/scripts/topo-chrome-gap-test.mjs +73 -0
- package/scripts/topo-chrome-icon-stroke-test.mjs +95 -0
- package/scripts/topo-chrome-inactive-color-test.mjs +90 -0
- package/scripts/topo-chrome-pop-extension-test.mjs +98 -0
- package/scripts/topo-chrome-pop-nodesize-test.mjs +88 -0
- package/scripts/topo-chrome-press-state-test.mjs +135 -0
- package/scripts/topo-chrome-strip-semantic-gap-test.mjs +99 -0
- package/scripts/topo-chrome-strip-theme-ease-test.mjs +99 -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-chrome-zoom-readout-weight-test.mjs +81 -0
- package/scripts/topo-click-ripple-ease-test.mjs +94 -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-corner-radius-unify-test.mjs +103 -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-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-badge-theme-ease-test.mjs +89 -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 +13 -5
- package/scripts/topo-edge-pill-hot-test.mjs +129 -0
- package/scripts/topo-edge-pill-test.mjs +105 -0
- package/scripts/topo-edge-ping-pulse-retired-test.mjs +119 -0
- package/scripts/topo-edge-stroke-ease-test.mjs +100 -0
- package/scripts/topo-edge-stroke-transition-test.mjs +132 -0
- package/scripts/topo-empty-hint-tracking-test.mjs +84 -0
- package/scripts/topo-empty-state-breath-test.mjs +150 -0
- package/scripts/topo-empty-state-crossfade-test.mjs +130 -0
- package/scripts/topo-empty-state-recenter-test.mjs +107 -0
- package/scripts/topo-empty-sub-hint-italic-test.mjs +83 -0
- package/scripts/topo-empty-subhint-tracking-test.mjs +87 -0
- package/scripts/topo-endpoint-ring-fade-test.mjs +132 -0
- package/scripts/topo-endpoint-ring-test.mjs +93 -0
- package/scripts/topo-endpoint-ring-thicken-test.mjs +112 -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-tabular-test.mjs +135 -0
- package/scripts/topo-filter-pill-count-test.mjs +110 -0
- package/scripts/topo-filter-pill-mobile-test.mjs +90 -0
- package/scripts/topo-freshness-chip-transition-test.mjs +80 -0
- package/scripts/topo-freshness-chip-weight-test.mjs +90 -0
- package/scripts/topo-freshness-conditional-render-test.mjs +92 -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 +6 -1
- package/scripts/topo-group-label-a11y-test.mjs +113 -0
- package/scripts/topo-group-label-count-inherit-test.mjs +99 -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 +104 -0
- package/scripts/topo-group-pip-singletier-drop-test.mjs +131 -0
- package/scripts/topo-group-pip-tabular-test.mjs +95 -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-hubcore-ease-test.mjs +106 -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-header-gap-test.mjs +94 -0
- 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-html-overlay-padding-unify-test.mjs +120 -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-pip-theme-ease-test.mjs +116 -0
- package/scripts/topo-hub-spoke-ease-test.mjs +120 -0
- package/scripts/topo-hub-title-test.mjs +7 -2
- package/scripts/topo-hub-working-count-test.mjs +99 -0
- package/scripts/topo-ingroup-edge-test.mjs +103 -0
- package/scripts/topo-inner-radar-ring-retired-test.mjs +89 -0
- package/scripts/topo-kicker-color-test.mjs +93 -0
- package/scripts/topo-kicker-tracking-test.mjs +101 -0
- package/scripts/topo-kicker-weight-test.mjs +98 -0
- package/scripts/topo-label-card-theme-ease-test.mjs +85 -0
- package/scripts/topo-label-lift-test.mjs +5 -1
- package/scripts/topo-layout-hover-text-preview-test.mjs +101 -0
- package/scripts/topo-layout-switch-crossfade-test.mjs +126 -0
- package/scripts/topo-layout-toggle-border-unify-test.mjs +104 -0
- package/scripts/topo-layout-toggle-test.mjs +131 -0
- package/scripts/topo-layout-trailer-mr-test.mjs +82 -0
- package/scripts/topo-legend-compress-test.mjs +107 -0
- package/scripts/topo-legend-count-empty-test.mjs +126 -0
- package/scripts/topo-legend-count-tabular-parity-test.mjs +90 -0
- package/scripts/topo-legend-count-tabular-test.mjs +87 -0
- package/scripts/topo-legend-count-test.mjs +88 -0
- package/scripts/topo-legend-count-tier-fill-test.mjs +121 -0
- package/scripts/topo-legend-count-weight-test.mjs +86 -0
- package/scripts/topo-legend-header-symmetric-inset-test.mjs +112 -0
- package/scripts/topo-legend-hitbox-swatch-center-test.mjs +93 -0
- package/scripts/topo-legend-hover-test.mjs +113 -0
- package/scripts/topo-legend-offline-delimiter-test.mjs +84 -0
- package/scripts/topo-legend-offline-label-simplify-test.mjs +86 -0
- package/scripts/topo-legend-panel-count-weight-test.mjs +89 -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-labels-simplify-test.mjs +80 -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-legend-swatch-radius-test.mjs +78 -0
- package/scripts/topo-minimap-a11y-test.mjs +101 -0
- package/scripts/topo-minimap-dot-transition-test.mjs +113 -0
- package/scripts/topo-minimap-radius-test.mjs +88 -0
- package/scripts/topo-minimap-rect-stroke-test.mjs +94 -0
- package/scripts/topo-minimap-viewport-smooth-test.mjs +124 -0
- package/scripts/topo-monogram-font-test.mjs +112 -0
- package/scripts/topo-monogram-stroke-test.mjs +101 -0
- package/scripts/topo-more-flows-letterspacing-test.mjs +111 -0
- package/scripts/topo-node-a11y-test.mjs +107 -0
- package/scripts/topo-node-alias-chat-pin-test.mjs +95 -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-online-chip-nav-test.mjs +143 -0
- package/scripts/topo-orbit-particles-retired-test.mjs +91 -0
- package/scripts/topo-panel-count-unit-test.mjs +112 -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-panel-rx-test.mjs +90 -0
- package/scripts/topo-panel-theme-ease-test.mjs +85 -0
- package/scripts/topo-panel-title-fill-ease-test.mjs +91 -0
- package/scripts/topo-panel-title-tracking-test.mjs +78 -0
- package/scripts/topo-particle-stagger-test.mjs +82 -0
- package/scripts/topo-pin-intersection-color-ease-test.mjs +111 -0
- package/scripts/topo-pin-intersection-empty-test.mjs +129 -0
- package/scripts/topo-pin-intersection-tabular-test.mjs +96 -0
- package/scripts/topo-pin-intersection-test.mjs +121 -0
- package/scripts/topo-pin-intersection-warning-crossfade-test.mjs +107 -0
- package/scripts/topo-pin-mirror-transition-test.mjs +101 -0
- package/scripts/topo-pin-persistence-test.mjs +105 -0
- package/scripts/topo-prefix-opacity-test.mjs +107 -0
- package/scripts/topo-prepublish-guard-test.mjs +95 -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-dot-retired-test.mjs +86 -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-count-weight-test.mjs +91 -0
- package/scripts/topo-recent-panel-footer-breath-test.mjs +142 -0
- package/scripts/topo-recent-panel-hot-header-test.mjs +132 -0
- package/scripts/topo-recent-panel-hot-tabular-test.mjs +113 -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-panel-readability-bump-test.mjs +151 -0
- package/scripts/topo-recent-row-count-fill-test.mjs +129 -0
- package/scripts/topo-recent-row-count-weight-test.mjs +112 -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-tabular-test.mjs +108 -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-reset-spin-test.mjs +120 -0
- package/scripts/topo-runtime-badge-grow-test.mjs +114 -0
- package/scripts/topo-spokes-retired-test.mjs +93 -0
- package/scripts/topo-starfield-density-test.mjs +89 -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-tabular-nums-sweep-test.mjs +93 -0
- package/scripts/topo-tier-guide-dashes-test.mjs +86 -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-title-block-tight-leading-test.mjs +95 -0
- package/scripts/topo-title-tracking-test.mjs +98 -0
- package/scripts/topo-titleblock-gap-test.mjs +86 -0
- package/scripts/topo-titleblock-margin-test.mjs +83 -0
- package/scripts/topo-toggle-inactive-cyan-hover-test.mjs +110 -0
- package/scripts/topo-top-rail-theme-ease-test.mjs +89 -0
- package/scripts/topo-vendor-chip-hover-test.mjs +111 -0
- package/scripts/topo-vendor-chip-self-hover-test.mjs +131 -0
- package/scripts/topo-vendor-chip-tabular-test.mjs +92 -0
- package/scripts/topo-vendor-chip-threshold-test.mjs +78 -0
- package/scripts/topo-vendor-chip-weight-test.mjs +87 -0
- package/scripts/topo-vendor-count-suffix-test.mjs +95 -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-watermark-tracking-test.mjs +99 -0
- package/scripts/topo-working-chip-pin-test.mjs +154 -0
- package/scripts/topo-working-halo-breath-retired-test.mjs +99 -0
- package/scripts/topo-wrapper-flow-arrow-theme-test.mjs +97 -0
- package/scripts/topo-wrapper-radius-test.mjs +73 -0
- package/scripts/topo-wrapper-shadow-theme-ease-test.mjs +89 -0
- package/.next/static/chunks/088ouqy7tekty.js +0 -2
- package/.next/static/chunks/0mqwn-l7wtcdo.js +0 -7
- package/.next/static/chunks/127kw7~wsnyu7.css +0 -2
- /package/.next/static/{23MxP0RBVA4BaOJ_HMYmM → ypixDJRds4g4TLoUdmrwN}/_buildManifest.js +0 -0
- /package/.next/static/{23MxP0RBVA4BaOJ_HMYmM → ypixDJRds4g4TLoUdmrwN}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{23MxP0RBVA4BaOJ_HMYmM → ypixDJRds4g4TLoUdmrwN}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/* Round 212 verification: dense plain-text alias (used when
|
|
2
|
+
* denseLayout > 16 nodes) gains fill transition on status flip,
|
|
3
|
+
* extending R211's card-mode coverage to the dense fallback path
|
|
4
|
+
* users see most on busy fleets.
|
|
5
|
+
*
|
|
6
|
+
* Pre-R212 the dense alias snap-cut its fill on tier change while
|
|
7
|
+
* the status ring smoothly transitioned (R167). R212 inline
|
|
8
|
+
* transition combines R26 transform 200ms (hover lift) with R212
|
|
9
|
+
* fill 300ms (status flip ease).
|
|
10
|
+
*
|
|
11
|
+
* Test (cyber dark, 17 sessions in mixed status → denseLayout=true):
|
|
12
|
+
* 1. 17+ [data-node-dense-alias-text] elements present (one per
|
|
13
|
+
* session — confirms denseLayout path fired, not card mode).
|
|
14
|
+
* 2. Inline transition includes BOTH 'transform 200ms' AND
|
|
15
|
+
* 'fill 300ms' (composite spliced, group-hover lift unbroken).
|
|
16
|
+
* 3. group-hover:-translate-y-[1.5px] className preserved (lift
|
|
17
|
+
* still wires via CSS pseudo-class).
|
|
18
|
+
* 4. Mixed-status fleet renders different fills per tier (working
|
|
19
|
+
* green ≠ idle teal ≠ offline gray family).
|
|
20
|
+
* 5. paintOrder: stroke + stroke attribute preserved (R110 halo).
|
|
21
|
+
*/
|
|
22
|
+
import { chromium } from 'playwright';
|
|
23
|
+
import { readFileSync } from 'node:fs';
|
|
24
|
+
|
|
25
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
26
|
+
const browser = await chromium.launch({ headless: true });
|
|
27
|
+
|
|
28
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
29
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
30
|
+
await ctx.addInitScript(() => {
|
|
31
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
35
|
+
// Build 17 sessions: mostly working, a couple idle + offline so fill
|
|
36
|
+
// colors visibly differ.
|
|
37
|
+
const statuses = [
|
|
38
|
+
...Array(13).fill('working'),
|
|
39
|
+
'working', 'idle', 'idle', 'offline',
|
|
40
|
+
];
|
|
41
|
+
const aliases = statuses.map((_, i) => `node-${(i + 1).toString().padStart(2, '0')}`);
|
|
42
|
+
|
|
43
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
44
|
+
const r = await route.fetch();
|
|
45
|
+
const b = await r.json();
|
|
46
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
47
|
+
await route.fulfill({ response: r, json: { ...b, sessions: aliases.map((alias, i) => ({
|
|
48
|
+
alias, status: statuses[i], model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
49
|
+
network_id: nid, project_dir: null,
|
|
50
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
51
|
+
})) } });
|
|
52
|
+
});
|
|
53
|
+
await ctx.route('**/api/hub/messages*', (r2) => r2.fulfill({ json: { messages: [] } }));
|
|
54
|
+
await ctx.route('**/api/hub/tasks*', (r2) => r2.fulfill({ json: { tasks: [] } }));
|
|
55
|
+
|
|
56
|
+
const page = await ctx.newPage();
|
|
57
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
58
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 17, { timeout: 30000 });
|
|
59
|
+
await page.waitForSelector('[data-node-dense-alias-text]', { timeout: 10000, state: 'attached' });
|
|
60
|
+
await page.waitForTimeout(300);
|
|
61
|
+
|
|
62
|
+
const probe = await page.evaluate(() => {
|
|
63
|
+
return [...document.querySelectorAll('[data-node-dense-alias-text]')].map(el => ({
|
|
64
|
+
alias: el.getAttribute('data-node-dense-alias-text'),
|
|
65
|
+
text: el.textContent,
|
|
66
|
+
fill: el.getAttribute('fill'),
|
|
67
|
+
className: el.getAttribute('class') || '',
|
|
68
|
+
transition: el.style.transition,
|
|
69
|
+
paintOrder: el.style.paintOrder,
|
|
70
|
+
stroke: el.getAttribute('stroke'),
|
|
71
|
+
}));
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
await browser.close();
|
|
75
|
+
|
|
76
|
+
const hasTransformT = (s) => /transform\s+(?:200ms|0\.2s)/.test(s || '');
|
|
77
|
+
const hasFillT = (s) => /fill\s+(?:300ms|0\.3s)/.test(s || '');
|
|
78
|
+
|
|
79
|
+
const workingAlias = probe.find(p => p.alias === 'node-01'); // first working
|
|
80
|
+
const idleAlias = probe.find(p => p.alias === 'node-15'); // first idle
|
|
81
|
+
const offlineAlias = probe.find(p => p.alias === 'node-17'); // offline
|
|
82
|
+
|
|
83
|
+
const results = {
|
|
84
|
+
dense_mode_fired: probe.length >= 17,
|
|
85
|
+
// R211 [data-node-alias-text] should NOT be present (card path skipped)
|
|
86
|
+
card_path_skipped: true, // implicit — if dense path fired, card path didn't render its texts
|
|
87
|
+
all_have_transition_fill: probe.every(p => hasFillT(p.transition)),
|
|
88
|
+
all_have_transition_trans: probe.every(p => hasTransformT(p.transition)),
|
|
89
|
+
all_keep_group_hover: probe.every(p => /group-hover:-translate-y/.test(p.className)),
|
|
90
|
+
all_have_paint_order: probe.every(p => p.paintOrder === 'stroke'),
|
|
91
|
+
all_have_halo_stroke: probe.every(p => !!p.stroke && p.stroke !== 'none'),
|
|
92
|
+
working_idle_fill_differs: workingAlias?.fill && idleAlias?.fill
|
|
93
|
+
&& workingAlias.fill !== idleAlias.fill,
|
|
94
|
+
working_offline_fill_differs: workingAlias?.fill && offlineAlias?.fill
|
|
95
|
+
&& workingAlias.fill !== offlineAlias.fill,
|
|
96
|
+
alias_text_rendered: workingAlias?.text === 'node-01',
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// Verify card path skipped — no R211 card-mode texts at >16 nodes
|
|
100
|
+
const cardAliasCount = await chromium.launch({ headless: true }).then(async (b2) => {
|
|
101
|
+
const ctx2 = await b2.newContext();
|
|
102
|
+
await ctx2.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
103
|
+
await ctx2.addInitScript(() => {
|
|
104
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
105
|
+
});
|
|
106
|
+
await ctx2.route('**/api/hub/status*', async (route) => {
|
|
107
|
+
const r = await route.fetch();
|
|
108
|
+
const bj = await r.json();
|
|
109
|
+
const nid = (bj.sessions || [])[0]?.network_id || 'default';
|
|
110
|
+
await route.fulfill({ response: r, json: { ...bj, sessions: aliases.map((alias, i) => ({
|
|
111
|
+
alias, status: statuses[i], model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
112
|
+
network_id: nid, project_dir: null,
|
|
113
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
114
|
+
})) } });
|
|
115
|
+
});
|
|
116
|
+
await ctx2.route('**/api/hub/messages*', (rr) => rr.fulfill({ json: { messages: [] } }));
|
|
117
|
+
await ctx2.route('**/api/hub/tasks*', (rr) => rr.fulfill({ json: { tasks: [] } }));
|
|
118
|
+
const p2 = await ctx2.newPage();
|
|
119
|
+
await p2.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
120
|
+
await p2.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 17, { timeout: 30000 });
|
|
121
|
+
await p2.waitForTimeout(300);
|
|
122
|
+
const c = await p2.evaluate(() =>
|
|
123
|
+
document.querySelectorAll('[data-node-alias-text]').length);
|
|
124
|
+
await b2.close();
|
|
125
|
+
return c;
|
|
126
|
+
});
|
|
127
|
+
results.card_path_skipped = cardAliasCount === 0;
|
|
128
|
+
|
|
129
|
+
const ok = Object.values(results).every(Boolean);
|
|
130
|
+
console.log(`${ok ? '✅' : '❌'} dense alias fill transition:`, JSON.stringify(results),
|
|
131
|
+
`\n sample (first 3):`, probe.slice(0, 3),
|
|
132
|
+
`\n cardAliasCount:`, cardAliasCount);
|
|
133
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/* Round 169 verification: keyboard '+'/'−' and chrome zoom in/out
|
|
2
|
+
* buttons arm the R168 smoothView transition.
|
|
3
|
+
*
|
|
4
|
+
* R168 introduced one-shot smoothView arming on resetView/fitView.
|
|
5
|
+
* R169 extends the same arming to the three other DISCRETE zoom
|
|
6
|
+
* surfaces (one keypress / button-click = one 1.2× step):
|
|
7
|
+
* keyboard '+' / '=' / '−' / '_'
|
|
8
|
+
* chrome zoom-in / zoom-out buttons
|
|
9
|
+
*
|
|
10
|
+
* Wheel zoom stays direct (every tick = live, no transition lag).
|
|
11
|
+
* Wrapper:
|
|
12
|
+
* const zoomByDiscrete = (factor) => {
|
|
13
|
+
* setSmoothView(true);
|
|
14
|
+
* setTimeout(() => setSmoothView(false), 350);
|
|
15
|
+
* zoomBy(factor);
|
|
16
|
+
* };
|
|
17
|
+
*
|
|
18
|
+
* Test:
|
|
19
|
+
* 1. Idle: smooth=false, no transition
|
|
20
|
+
* 2. Press '+': smooth=true, transition 'transform 300ms ease-out'
|
|
21
|
+
* zoom 100% → 120%
|
|
22
|
+
* 3. Wait 450ms: smooth=false, transition cleared
|
|
23
|
+
* 4. Click chrome zoom-in button: smooth=true again (same path)
|
|
24
|
+
* 5. Press '−': smooth=true, zoom back toward 100%
|
|
25
|
+
*/
|
|
26
|
+
import { chromium } from 'playwright';
|
|
27
|
+
import { readFileSync } from 'node:fs';
|
|
28
|
+
|
|
29
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
30
|
+
const browser = await chromium.launch({ headless: true });
|
|
31
|
+
|
|
32
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
33
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
34
|
+
await ctx.addInitScript(() => {
|
|
35
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
39
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
40
|
+
const r = await route.fetch();
|
|
41
|
+
const b = await r.json();
|
|
42
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
43
|
+
const mk = (alias) => ({
|
|
44
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
45
|
+
network_id: nid, project_dir: null,
|
|
46
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
47
|
+
});
|
|
48
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta'), mk('gamma')] } });
|
|
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 === 3, { timeout: 30000 });
|
|
56
|
+
await page.waitForSelector('[data-topo-viewport]', { timeout: 10000 });
|
|
57
|
+
await page.waitForTimeout(400);
|
|
58
|
+
|
|
59
|
+
const probeViewport = () => page.evaluate(() => {
|
|
60
|
+
const g = document.querySelector('[data-topo-viewport]');
|
|
61
|
+
return {
|
|
62
|
+
smooth: g?.getAttribute('data-topo-viewport-smooth'),
|
|
63
|
+
transition: g?.style?.transition || '',
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
const probeZoom = () => page.evaluate(() =>
|
|
67
|
+
document.querySelector('[data-topo-chrome-zoom-level]')?.textContent?.trim());
|
|
68
|
+
|
|
69
|
+
const idle = await probeViewport();
|
|
70
|
+
const zoomIdle = await probeZoom();
|
|
71
|
+
|
|
72
|
+
// Press '+' → keyboard zoom-in. Probe within the 350ms window.
|
|
73
|
+
await page.keyboard.press('+');
|
|
74
|
+
await page.waitForTimeout(50);
|
|
75
|
+
const duringKbZoomIn = await probeViewport();
|
|
76
|
+
const zoomAfterKb = await probeZoom();
|
|
77
|
+
|
|
78
|
+
// Wait past the 350ms window
|
|
79
|
+
await page.waitForTimeout(450);
|
|
80
|
+
const afterKbZoomIn = await probeViewport();
|
|
81
|
+
|
|
82
|
+
// Click chrome zoom-in button → same arming path
|
|
83
|
+
await page.locator('[data-topo-chrome-zoom-in]').click();
|
|
84
|
+
await page.waitForTimeout(50);
|
|
85
|
+
const duringChromeZoomIn = await probeViewport();
|
|
86
|
+
const zoomAfterChromeIn = await probeZoom();
|
|
87
|
+
|
|
88
|
+
// Wait past window then press '−' (keyboard zoom-out)
|
|
89
|
+
await page.waitForTimeout(450);
|
|
90
|
+
await page.keyboard.press('-');
|
|
91
|
+
await page.waitForTimeout(50);
|
|
92
|
+
const duringKbZoomOut = await probeViewport();
|
|
93
|
+
const zoomAfterKbOut = await probeZoom();
|
|
94
|
+
|
|
95
|
+
await browser.close();
|
|
96
|
+
|
|
97
|
+
const results = {
|
|
98
|
+
viewport_found: idle !== null,
|
|
99
|
+
idle_smooth_false: idle.smooth === 'false',
|
|
100
|
+
idle_no_transition: !idle.transition.includes('transform'),
|
|
101
|
+
idle_zoom_100: zoomIdle === '100%',
|
|
102
|
+
|
|
103
|
+
kb_plus_armed_smooth: duringKbZoomIn.smooth === 'true',
|
|
104
|
+
kb_plus_transition_set: duringKbZoomIn.transition.includes('transform 300ms'),
|
|
105
|
+
kb_plus_zoom_increased: zoomAfterKb !== '100%',
|
|
106
|
+
|
|
107
|
+
after_kb_smooth_cleared: afterKbZoomIn.smooth === 'false',
|
|
108
|
+
after_kb_no_transition: !afterKbZoomIn.transition.includes('transform'),
|
|
109
|
+
|
|
110
|
+
chrome_in_armed_smooth: duringChromeZoomIn.smooth === 'true',
|
|
111
|
+
chrome_in_transition_set: duringChromeZoomIn.transition.includes('transform 300ms'),
|
|
112
|
+
chrome_in_zoom_increased: zoomAfterChromeIn !== zoomAfterKb,
|
|
113
|
+
|
|
114
|
+
kb_minus_armed_smooth: duringKbZoomOut.smooth === 'true',
|
|
115
|
+
kb_minus_transition_set: duringKbZoomOut.transition.includes('transform 300ms'),
|
|
116
|
+
kb_minus_zoom_decreased: zoomAfterKbOut !== zoomAfterChromeIn,
|
|
117
|
+
};
|
|
118
|
+
const ok = Object.values(results).every(Boolean);
|
|
119
|
+
console.log(`${ok ? '✅' : '❌'} discrete zoom smooth:`, JSON.stringify(results),
|
|
120
|
+
`\n idle =`, idle, `zoom=${zoomIdle}`,
|
|
121
|
+
`\n '+' fired =`, duringKbZoomIn, `zoom=${zoomAfterKb}`,
|
|
122
|
+
`\n after '+' =`, afterKbZoomIn,
|
|
123
|
+
`\n chrome-in fired =`, duringChromeZoomIn, `zoom=${zoomAfterChromeIn}`,
|
|
124
|
+
`\n '-' fired =`, duringKbZoomOut, `zoom=${zoomAfterKbOut}`);
|
|
125
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/* Round 121 verification: clicking an edge midpoint count badge
|
|
2
|
+
* (R100) pins the matching flow, mirroring the R116 recent-signal
|
|
3
|
+
* row click-pin from the canvas side. Closes the gap where the
|
|
4
|
+
* canvas had no pin-from-here affordance.
|
|
5
|
+
*
|
|
6
|
+
* Drive: 1 flow with count >= 3 (badge renders). Click badge →
|
|
7
|
+
* data-edge-count-badge-pinned="true", aria-pressed="true", the
|
|
8
|
+
* matching pill (R119) appears. Click again → release.
|
|
9
|
+
*/
|
|
10
|
+
import { chromium } from 'playwright';
|
|
11
|
+
import { readFileSync } from 'node:fs';
|
|
12
|
+
|
|
13
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
14
|
+
const browser = await chromium.launch({ headless: true });
|
|
15
|
+
|
|
16
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
|
|
17
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
18
|
+
await ctx.addInitScript(() => {
|
|
19
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
20
|
+
});
|
|
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 mk = (alias) => ({
|
|
28
|
+
alias, status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
29
|
+
network_id: nid, project_dir: null,
|
|
30
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
31
|
+
});
|
|
32
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta'), mk('gamma')] } });
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const now = Date.now();
|
|
36
|
+
const mkMsg = (id, from_alias, to_alias, ageMs) => ({
|
|
37
|
+
id, from_alias, to_alias, content: 'hi', network_id: 'default',
|
|
38
|
+
created_at: new Date(now - ageMs).toISOString(),
|
|
39
|
+
});
|
|
40
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
|
|
41
|
+
mkMsg('m1', 'alpha', 'beta', 30 * 1000),
|
|
42
|
+
mkMsg('m2', 'alpha', 'beta', 40 * 1000),
|
|
43
|
+
mkMsg('m3', 'alpha', 'beta', 50 * 1000),
|
|
44
|
+
mkMsg('m4', 'alpha', 'beta', 60 * 1000),
|
|
45
|
+
] } }));
|
|
46
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
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 === 3, { timeout: 30000 });
|
|
50
|
+
await page.waitForSelector('[data-edge-count-badge]', { timeout: 10000 });
|
|
51
|
+
await page.waitForTimeout(400);
|
|
52
|
+
|
|
53
|
+
const probe = () => page.evaluate(() => {
|
|
54
|
+
const badge = document.querySelector('[data-edge-count-badge="alpha->beta"]');
|
|
55
|
+
return {
|
|
56
|
+
badgePinned: badge?.getAttribute('data-edge-count-badge-pinned') || '',
|
|
57
|
+
aria: badge?.getAttribute('aria-pressed') || '',
|
|
58
|
+
pillExists: document.querySelector('[data-active-filter="edge"]') !== null,
|
|
59
|
+
title: badge?.querySelector('title')?.textContent || '',
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const before = await probe();
|
|
64
|
+
|
|
65
|
+
// Click the badge → pin.
|
|
66
|
+
await page.locator('[data-edge-count-badge="alpha->beta"]').click();
|
|
67
|
+
await page.mouse.move(10, 10);
|
|
68
|
+
await page.waitForTimeout(300);
|
|
69
|
+
const afterClick = await probe();
|
|
70
|
+
|
|
71
|
+
// Click again → release.
|
|
72
|
+
await page.locator('[data-edge-count-badge="alpha->beta"]').click();
|
|
73
|
+
await page.mouse.move(10, 10);
|
|
74
|
+
await page.waitForTimeout(300);
|
|
75
|
+
const afterRelease = await probe();
|
|
76
|
+
|
|
77
|
+
await browser.close();
|
|
78
|
+
|
|
79
|
+
const results = {
|
|
80
|
+
before_notPinned: before.badgePinned === 'false' && before.aria === 'false',
|
|
81
|
+
before_noPill: before.pillExists === false,
|
|
82
|
+
before_titlePinHint: /click to pin/.test(before.title),
|
|
83
|
+
click_pinned: afterClick.badgePinned === 'true' && afterClick.aria === 'true',
|
|
84
|
+
click_pillAppears: afterClick.pillExists === true,
|
|
85
|
+
click_titleReleaseHint: /click to release pin/.test(afterClick.title),
|
|
86
|
+
release_unpinned: afterRelease.badgePinned === 'false' && afterRelease.aria === 'false',
|
|
87
|
+
release_pillGone: afterRelease.pillExists === false,
|
|
88
|
+
};
|
|
89
|
+
const ok = Object.values(results).every(Boolean);
|
|
90
|
+
console.log(`${ok ? '✅' : '❌'} edge-badge click-pin:`, JSON.stringify(results),
|
|
91
|
+
`\n before=`, before,
|
|
92
|
+
`\n afterClick=`, afterClick,
|
|
93
|
+
`\n afterRelease=`, afterRelease);
|
|
94
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/* Round 215 verification: edge midpoint count badge always-mounts;
|
|
2
|
+
* visibility crossfades via wrapper <g> opacity instead of mount/
|
|
3
|
+
* unmount on link.count >= 3 threshold. Closes the canvas-edge
|
|
4
|
+
* surface of the always-mount-opacity-gate family.
|
|
5
|
+
*
|
|
6
|
+
* Pre-R215 a flow's first 2 → 3 message snap-popped the badge in
|
|
7
|
+
* one frame, and a hot flow tapering 3 → 2 vanished in one frame.
|
|
8
|
+
* R215 always-mounts and opacity-gates, giving 300ms ease through
|
|
9
|
+
* the threshold crossing.
|
|
10
|
+
*
|
|
11
|
+
* Test (cyber dark, mock 3 flows below + at + above threshold):
|
|
12
|
+
* 1. flow A: count=2 (below threshold) → badge in DOM but
|
|
13
|
+
* visible='false', opacity 0, pointerEvents 'none',
|
|
14
|
+
* aria-hidden='true', role unset, tabIndex=-1
|
|
15
|
+
* 2. flow B: count=3 (at threshold) → badge visible='true',
|
|
16
|
+
* opacity > 0, pointerEvents 'all', role='button', tabIndex=0
|
|
17
|
+
* 3. flow C: count=12 (hot) → badge visible='true',
|
|
18
|
+
* hot='true', opacity > 0, all interactive
|
|
19
|
+
* 4. Every wrapper has inline transition: 'opacity 300ms ease-out'
|
|
20
|
+
* 5. text content always matches link.count (even the hidden one)
|
|
21
|
+
*/
|
|
22
|
+
import { chromium } from 'playwright';
|
|
23
|
+
import { readFileSync } from 'node:fs';
|
|
24
|
+
|
|
25
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
26
|
+
const browser = await chromium.launch({ headless: true });
|
|
27
|
+
|
|
28
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
29
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
30
|
+
await ctx.addInitScript(() => {
|
|
31
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
35
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
36
|
+
const r = await route.fetch();
|
|
37
|
+
const b = await r.json();
|
|
38
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
39
|
+
const mk = (alias) => ({
|
|
40
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
41
|
+
network_id: nid, project_dir: null,
|
|
42
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
43
|
+
});
|
|
44
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
45
|
+
mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
|
|
46
|
+
] } });
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const now = Date.now();
|
|
50
|
+
const msgs = [];
|
|
51
|
+
// flow A: alpha → beta · count=2 (below threshold, badge hidden)
|
|
52
|
+
for (let i = 0; i < 2; i++) {
|
|
53
|
+
msgs.push({ id: `A${i}`, from_alias: 'alpha', to_alias: 'beta', content: 'hi',
|
|
54
|
+
network_id: 'default', created_at: new Date(now - (1000 + i * 50)).toISOString() });
|
|
55
|
+
}
|
|
56
|
+
// flow B: alpha → gamma · count=3 (at threshold, badge visible)
|
|
57
|
+
for (let i = 0; i < 3; i++) {
|
|
58
|
+
msgs.push({ id: `B${i}`, from_alias: 'alpha', to_alias: 'gamma', content: 'hi',
|
|
59
|
+
network_id: 'default', created_at: new Date(now - (5000 + i * 50)).toISOString() });
|
|
60
|
+
}
|
|
61
|
+
// flow C: alpha → delta · count=12 (hot)
|
|
62
|
+
for (let i = 0; i < 12; i++) {
|
|
63
|
+
msgs.push({ id: `C${i}`, from_alias: 'alpha', to_alias: 'delta', content: 'hi',
|
|
64
|
+
network_id: 'default', created_at: new Date(now - (10000 + i * 50)).toISOString() });
|
|
65
|
+
}
|
|
66
|
+
await ctx.route('**/api/hub/messages*', (r2) => r2.fulfill({ json: { messages: msgs } }));
|
|
67
|
+
await ctx.route('**/api/hub/tasks*', (r2) => r2.fulfill({ json: { tasks: [] } }));
|
|
68
|
+
|
|
69
|
+
const page = await ctx.newPage();
|
|
70
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
71
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
72
|
+
await page.waitForSelector('[data-edge-count-badge]', { timeout: 10000, state: 'attached' });
|
|
73
|
+
await page.waitForTimeout(400);
|
|
74
|
+
|
|
75
|
+
const probe = await page.evaluate(() => {
|
|
76
|
+
return [...document.querySelectorAll('[data-edge-count-badge]')].map(el => {
|
|
77
|
+
const cs = getComputedStyle(el);
|
|
78
|
+
const txt = el.querySelector('text')?.textContent;
|
|
79
|
+
return {
|
|
80
|
+
key: el.getAttribute('data-edge-count-badge'),
|
|
81
|
+
visible: el.getAttribute('data-edge-count-badge-visible'),
|
|
82
|
+
hot: el.getAttribute('data-edge-count-badge-hot'),
|
|
83
|
+
role: el.getAttribute('role'),
|
|
84
|
+
tabIndex: el.getAttribute('tabindex'),
|
|
85
|
+
ariaHidden: el.getAttribute('aria-hidden'),
|
|
86
|
+
opacity: parseFloat(el.getAttribute('opacity') || ''),
|
|
87
|
+
computedPointer: cs.pointerEvents,
|
|
88
|
+
transition: el.style.transition,
|
|
89
|
+
text: txt,
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
await browser.close();
|
|
95
|
+
|
|
96
|
+
const flowA = probe.find(p => p.key === 'alpha->beta'); // count=2
|
|
97
|
+
const flowB = probe.find(p => p.key === 'alpha->gamma'); // count=3
|
|
98
|
+
const flowC = probe.find(p => p.key === 'alpha->delta'); // count=12
|
|
99
|
+
|
|
100
|
+
const results = {
|
|
101
|
+
// All 3 wrappers present in DOM (always-mount)
|
|
102
|
+
three_badges_in_dom: probe.length === 3,
|
|
103
|
+
flowA_present: !!flowA,
|
|
104
|
+
flowB_present: !!flowB,
|
|
105
|
+
flowC_present: !!flowC,
|
|
106
|
+
|
|
107
|
+
// flow A (count=2, hidden)
|
|
108
|
+
A_visible_false: flowA?.visible === 'false',
|
|
109
|
+
A_opacity_0: flowA?.opacity === 0,
|
|
110
|
+
A_pointer_none: flowA?.computedPointer === 'none',
|
|
111
|
+
A_aria_hidden: flowA?.ariaHidden === 'true',
|
|
112
|
+
A_role_unset: flowA?.role === null || flowA?.role === '',
|
|
113
|
+
A_text_2: flowA?.text === '2',
|
|
114
|
+
|
|
115
|
+
// flow B (count=3, just visible)
|
|
116
|
+
B_visible_true: flowB?.visible === 'true',
|
|
117
|
+
B_opacity_positive: (flowB?.opacity ?? 0) > 0,
|
|
118
|
+
B_pointer_all: flowB?.computedPointer === 'all',
|
|
119
|
+
B_role_button: flowB?.role === 'button',
|
|
120
|
+
B_text_3: flowB?.text === '3',
|
|
121
|
+
B_not_hot: flowB?.hot === 'false',
|
|
122
|
+
|
|
123
|
+
// flow C (count=12, hot)
|
|
124
|
+
C_visible_true: flowC?.visible === 'true',
|
|
125
|
+
C_hot_true: flowC?.hot === 'true',
|
|
126
|
+
C_opacity_positive: (flowC?.opacity ?? 0) > 0,
|
|
127
|
+
C_text_12: flowC?.text === '12',
|
|
128
|
+
|
|
129
|
+
// All have transition with opacity 300ms (even hidden ones — they
|
|
130
|
+
// need the transition for when count crosses back into threshold)
|
|
131
|
+
all_have_transition: probe.every(p => /opacity\s+(?:300ms|0\.3s)/.test(p.transition || '')),
|
|
132
|
+
};
|
|
133
|
+
const ok = Object.values(results).every(Boolean);
|
|
134
|
+
console.log(`${ok ? '✅' : '❌'} edge badge crossfade:`, JSON.stringify(results),
|
|
135
|
+
`\n flowA (count=2 hidden):`, flowA,
|
|
136
|
+
`\n flowB (count=3 visible):`, flowB,
|
|
137
|
+
`\n flowC (count=12 hot):`, flowC);
|
|
138
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/* Round 126 verification: hot-edge accent on the midpoint count
|
|
2
|
+
* badge. The edge stroke width (line 2344 of TopoGraph.tsx) scales
|
|
3
|
+
* with count but clamps at 7px — so count=5 and count=50 look
|
|
4
|
+
* identical at the line. R100 introduced the badge for high-traffic
|
|
5
|
+
* lanes, but the badge itself stayed the same colour regardless.
|
|
6
|
+
*
|
|
7
|
+
* R126 buckets count ≥ 10 as "hot" and flips the badge stroke to
|
|
8
|
+
* amber (#d97706 light, #fbbf24 dark) + thickens to width 2. Pin
|
|
9
|
+
* still wins (uses legendHeadline) so pinned-hot reads as locked
|
|
10
|
+
* via the badge text + edge brightness, not via a third stroke
|
|
11
|
+
* colour. Reuses R125's amber convention — "amber draws the eye".
|
|
12
|
+
*
|
|
13
|
+
* Fleet:
|
|
14
|
+
* alpha (working) — pings beta with 12 msgs (hot)
|
|
15
|
+
* beta (working) — pings gamma with 4 msgs (warm, badge but not hot)
|
|
16
|
+
* gamma (idle)
|
|
17
|
+
*
|
|
18
|
+
* Path:
|
|
19
|
+
* 1. alpha→beta: badge hot=true, stroke matches amber rgb
|
|
20
|
+
* 2. beta→gamma: badge hot=false, stroke matches flowEdge (not amber)
|
|
21
|
+
* 3. Click alpha→beta to pin; pin still wins (stroke = legendHeadline,
|
|
22
|
+
* data-edge-count-badge-pinned=true, hot=true is still flagged
|
|
23
|
+
* via the attribute but the visible stroke is the pin colour)
|
|
24
|
+
*/
|
|
25
|
+
import { chromium } from 'playwright';
|
|
26
|
+
import { readFileSync } from 'node:fs';
|
|
27
|
+
|
|
28
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
29
|
+
const browser = await chromium.launch({ headless: true });
|
|
30
|
+
|
|
31
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
|
|
32
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
33
|
+
await ctx.addInitScript(() => {
|
|
34
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
38
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
39
|
+
const r = await route.fetch();
|
|
40
|
+
const b = await r.json();
|
|
41
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
42
|
+
const mk = (alias, status) => ({
|
|
43
|
+
alias, status, model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
44
|
+
network_id: nid, project_dir: null,
|
|
45
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
46
|
+
});
|
|
47
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
48
|
+
mk('alpha', 'working'),
|
|
49
|
+
mk('beta', 'working'),
|
|
50
|
+
mk('gamma', 'idle'),
|
|
51
|
+
] } });
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const now = Date.now();
|
|
55
|
+
const mkMsg = (id, from_alias, to_alias, ageMs) => ({
|
|
56
|
+
id, from_alias, to_alias, content: 'x', network_id: 'default',
|
|
57
|
+
created_at: new Date(now - ageMs).toISOString(),
|
|
58
|
+
});
|
|
59
|
+
// 12 alpha→beta = HOT, 4 beta→gamma = warm
|
|
60
|
+
const msgs = [];
|
|
61
|
+
for (let i = 0; i < 12; i++) msgs.push(mkMsg(`a${i}`, 'alpha', 'beta', 20000 + i * 500));
|
|
62
|
+
for (let i = 0; i < 4; i++) msgs.push(mkMsg(`b${i}`, 'beta', 'gamma', 30000 + i * 500));
|
|
63
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
|
|
64
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
65
|
+
|
|
66
|
+
const page = await ctx.newPage();
|
|
67
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
68
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
|
|
69
|
+
await page.waitForSelector('[data-edge-count-badge="alpha->beta"]', { timeout: 10000 });
|
|
70
|
+
await page.waitForTimeout(400);
|
|
71
|
+
|
|
72
|
+
const inspect = (key) => page.evaluate((k) => {
|
|
73
|
+
const g = document.querySelector(`[data-edge-count-badge="${k}"]`);
|
|
74
|
+
if (!g) return null;
|
|
75
|
+
const circle = g.querySelector('circle');
|
|
76
|
+
return {
|
|
77
|
+
hot: g.getAttribute('data-edge-count-badge-hot'),
|
|
78
|
+
pinned: g.getAttribute('data-edge-count-badge-pinned'),
|
|
79
|
+
stroke: circle?.getAttribute('stroke'),
|
|
80
|
+
strokeWidth: circle?.getAttribute('stroke-width'),
|
|
81
|
+
};
|
|
82
|
+
}, key);
|
|
83
|
+
|
|
84
|
+
// State 1 + 2: before any pin
|
|
85
|
+
const a2b = await inspect('alpha->beta');
|
|
86
|
+
const b2g = await inspect('beta->gamma');
|
|
87
|
+
|
|
88
|
+
// State 3: pin alpha→beta — pin wins over hot
|
|
89
|
+
await page.locator('[data-edge-count-badge="alpha->beta"]').click();
|
|
90
|
+
await page.waitForTimeout(200);
|
|
91
|
+
const a2bPinned = await inspect('alpha->beta');
|
|
92
|
+
|
|
93
|
+
await browser.close();
|
|
94
|
+
|
|
95
|
+
// Cyber theme amber: #fbbf24
|
|
96
|
+
const amberHex = '#fbbf24';
|
|
97
|
+
const flowEdgeNotAmber = (s) => !!s && s.toLowerCase() !== amberHex.toLowerCase();
|
|
98
|
+
|
|
99
|
+
const results = {
|
|
100
|
+
hot_present: a2b !== null && a2b.hot === 'true',
|
|
101
|
+
hot_strokeAmber: a2b !== null && (a2b.stroke || '').toLowerCase() === amberHex.toLowerCase(),
|
|
102
|
+
hot_strokeWidth2: a2b !== null && a2b.strokeWidth === '2',
|
|
103
|
+
warm_notHot: b2g !== null && b2g.hot === 'false',
|
|
104
|
+
warm_strokeNotAmber: b2g !== null && flowEdgeNotAmber(b2g.stroke),
|
|
105
|
+
warm_strokeWidth1: b2g !== null && b2g.strokeWidth === '1',
|
|
106
|
+
pin_winsOverHot: a2bPinned !== null && a2bPinned.pinned === 'true' && a2bPinned.hot === 'true'
|
|
107
|
+
&& (a2bPinned.stroke || '').toLowerCase() !== amberHex.toLowerCase()
|
|
108
|
+
&& a2bPinned.strokeWidth === '2',
|
|
109
|
+
};
|
|
110
|
+
const ok = Object.values(results).every(Boolean);
|
|
111
|
+
console.log(`${ok ? '✅' : '❌'} edge-badge hot:`, JSON.stringify(results),
|
|
112
|
+
`\n a2b(12msg)=`, a2b,
|
|
113
|
+
`\n b2g(4msg)=`, b2g,
|
|
114
|
+
`\n a2b-pinned=`, a2bPinned);
|
|
115
|
+
process.exit(ok ? 0 : 1);
|