@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,86 @@
|
|
|
1
|
+
/* Round 100 verification: edges with count >= 3 render a midpoint
|
|
2
|
+
* count badge. The edge stroke width gauges intensity but at high
|
|
3
|
+
* volume "5 vs 12" is hard to read off the line. The badge spells
|
|
4
|
+
* out the integer at the bezier midpoint.
|
|
5
|
+
*
|
|
6
|
+
* Fleet: alpha → beta with 5 messages, gamma → delta with 1, all
|
|
7
|
+
* fresh. Expect one badge for the 5-msg flow, none for the 1-msg
|
|
8
|
+
* flow (count below threshold). Verify badge text matches the
|
|
9
|
+
* link.count and pointer-events:none so the existing R48 edge
|
|
10
|
+
* hitbox stays alive.
|
|
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
|
+
|
|
18
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
|
|
19
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
20
|
+
await ctx.addInitScript(() => {
|
|
21
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const fresh = new Date(Date.now() - 30 * 1000).toISOString();
|
|
25
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
26
|
+
const r = await route.fetch();
|
|
27
|
+
const b = await r.json();
|
|
28
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
29
|
+
const mk = (alias) => ({
|
|
30
|
+
alias, status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
31
|
+
network_id: nid, project_dir: null,
|
|
32
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
33
|
+
});
|
|
34
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta'), mk('gamma'), mk('delta')] } });
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const now = Date.now();
|
|
38
|
+
const mkMsg = (id, from_alias, to_alias, ageMs) => ({
|
|
39
|
+
id, from_alias, to_alias, content: 'ping', network_id: 'default',
|
|
40
|
+
created_at: new Date(now - ageMs).toISOString(),
|
|
41
|
+
});
|
|
42
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
|
|
43
|
+
// 5 alpha → beta (count=5, badge expected)
|
|
44
|
+
mkMsg('m1', 'alpha', 'beta', 30 * 1000),
|
|
45
|
+
mkMsg('m2', 'alpha', 'beta', 40 * 1000),
|
|
46
|
+
mkMsg('m3', 'alpha', 'beta', 50 * 1000),
|
|
47
|
+
mkMsg('m4', 'alpha', 'beta', 60 * 1000),
|
|
48
|
+
mkMsg('m5', 'alpha', 'beta', 70 * 1000),
|
|
49
|
+
// 1 gamma → delta (count=1, NO badge)
|
|
50
|
+
mkMsg('m6', 'gamma', 'delta', 80 * 1000),
|
|
51
|
+
// 3 beta → alpha (count=3 — threshold, badge expected)
|
|
52
|
+
mkMsg('m7', 'beta', 'alpha', 35 * 1000),
|
|
53
|
+
mkMsg('m8', 'beta', 'alpha', 45 * 1000),
|
|
54
|
+
mkMsg('m9', 'beta', 'alpha', 55 * 1000),
|
|
55
|
+
] } }));
|
|
56
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
57
|
+
const page = await ctx.newPage();
|
|
58
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
59
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
60
|
+
await page.waitForSelector('[data-edge-count-badge]', { timeout: 10000 });
|
|
61
|
+
await page.waitForTimeout(400);
|
|
62
|
+
|
|
63
|
+
const badges = await page.evaluate(() => {
|
|
64
|
+
return [...document.querySelectorAll('[data-edge-count-badge]')].map(g => ({
|
|
65
|
+
key: g.getAttribute('data-edge-count-badge'),
|
|
66
|
+
count: g.querySelector('text')?.textContent || '',
|
|
67
|
+
pe: getComputedStyle(g).pointerEvents,
|
|
68
|
+
}));
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
await browser.close();
|
|
72
|
+
|
|
73
|
+
const byKey = Object.fromEntries(badges.map(b => [b.key, b]));
|
|
74
|
+
const results = {
|
|
75
|
+
twoBadges: badges.length === 2,
|
|
76
|
+
alphaBeta_badge: byKey['alpha->beta']?.count === '5',
|
|
77
|
+
betaAlpha_badge: byKey['beta->alpha']?.count === '3',
|
|
78
|
+
gammaDelta_noBadge: !byKey['gamma->delta'],
|
|
79
|
+
// R121 promoted the badge from passive display to a click-to-pin
|
|
80
|
+
// affordance — assertion flipped from non-interactive to interactive.
|
|
81
|
+
badges_interactive: badges.every(b => b.pe === 'all'),
|
|
82
|
+
};
|
|
83
|
+
const ok = Object.values(results).every(Boolean);
|
|
84
|
+
console.log(`${ok ? '✅' : '❌'} edge-count badge:`, JSON.stringify(results),
|
|
85
|
+
`\n badges=`, badges);
|
|
86
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/* Round 49 verification: hovering a flow edge highlights its two endpoint
|
|
2
|
+
* nodes and dims the rest — the reverse direction of R40 (node→edges) so
|
|
3
|
+
* "who is this edge between" reads at a glance without the tooltip.
|
|
4
|
+
*
|
|
5
|
+
* Sessions: alpha, beta, gamma, delta. Messages: alpha→beta + gamma→delta.
|
|
6
|
+
* - Before hover: all 4 nodes at base opacity (~1.0).
|
|
7
|
+
* - Hover alpha→beta hitbox: alpha + beta opacity ≥ 0.6, gamma + delta
|
|
8
|
+
* drop below 0.4 (composed via `hoveredEdgeEndpoints`).
|
|
9
|
+
* - Move off the edge: opacities return to base.
|
|
10
|
+
*/
|
|
11
|
+
import { chromium } from 'playwright';
|
|
12
|
+
import { readFileSync } from 'node:fs';
|
|
13
|
+
|
|
14
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
15
|
+
const browser = await chromium.launch({ headless: true });
|
|
16
|
+
|
|
17
|
+
// The dashboard chrome (sidebar + header + stats row) pushes the topology
|
|
18
|
+
// SVG well below the fold at width 1280. Height 1500 fits the whole canvas
|
|
19
|
+
// so SVG-local coordinates map to in-viewport screen coordinates.
|
|
20
|
+
const ctx = await browser.newContext({ viewport: { width: 1280, height: 1500 } });
|
|
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
|
+
// Ring layout places nodes around the SVG center and edges curve through
|
|
26
|
+
// it — well inside the 1280x900 viewport. Grid layout pushes the lower-
|
|
27
|
+
// row edges below the fold and breaks locator.hover positioning.
|
|
28
|
+
localStorage.setItem('anet-topo-layout', 'ring');
|
|
29
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
30
|
+
} catch {}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
34
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
35
|
+
const r = await route.fetch();
|
|
36
|
+
const b = await r.json();
|
|
37
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
38
|
+
const sessions = ['alpha', 'beta', 'gamma', 'delta'].map(a => ({
|
|
39
|
+
alias: a, status: 'idle', network_id: nid, project_dir: null,
|
|
40
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
41
|
+
}));
|
|
42
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
43
|
+
});
|
|
44
|
+
const now = new Date().toISOString();
|
|
45
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
|
|
46
|
+
{ from_alias: 'alpha', to_alias: 'beta', content: 'm', created_at: now },
|
|
47
|
+
{ from_alias: 'gamma', to_alias: 'delta', content: 'm', created_at: now },
|
|
48
|
+
] } }));
|
|
49
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
50
|
+
const page = await ctx.newPage();
|
|
51
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
52
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
53
|
+
await page.waitForTimeout(600);
|
|
54
|
+
|
|
55
|
+
const opacities = () => page.evaluate(() => {
|
|
56
|
+
const out = {};
|
|
57
|
+
for (const a of ['alpha', 'beta', 'gamma', 'delta']) {
|
|
58
|
+
const g = document.querySelector(`g[data-node="${a}"]`);
|
|
59
|
+
out[a] = g ? +(g.style.opacity || '1') : null;
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const before = await opacities();
|
|
65
|
+
|
|
66
|
+
// Hover the alpha→beta edge hitbox. The hitbox is a curved <path> with
|
|
67
|
+
// `pointer-events: stroke`, so the cursor only triggers events when it
|
|
68
|
+
// sits on the stroke itself — the bbox centroid is inside an empty region.
|
|
69
|
+
// Sample a real point at half the path's length via getPointAtLength and
|
|
70
|
+
// convert SVG-local coordinates to screen via getScreenCTM, then move the
|
|
71
|
+
// physical mouse there.
|
|
72
|
+
const target = await page.evaluate(() => {
|
|
73
|
+
const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
|
|
74
|
+
for (const g of svg.querySelectorAll(':scope > g > g')) {
|
|
75
|
+
const t = g.querySelector('path[data-edge-hitbox] title');
|
|
76
|
+
if (t && /^alpha → beta/.test(t.textContent || '')) {
|
|
77
|
+
const hb = g.querySelector('path[data-edge-hitbox]');
|
|
78
|
+
const pt = hb.getPointAtLength(hb.getTotalLength() / 2);
|
|
79
|
+
const ctm = hb.getScreenCTM();
|
|
80
|
+
return { x: pt.x * ctm.a + pt.y * ctm.c + ctm.e, y: pt.x * ctm.b + pt.y * ctm.d + ctm.f };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
});
|
|
85
|
+
if (!target) { console.log('❌ no alpha→beta hitbox found'); process.exit(1); }
|
|
86
|
+
console.log(' hover target:', target);
|
|
87
|
+
// Move twice — first to an empty spot, then to the curve point — so the
|
|
88
|
+
// browser registers a pointermove from "elsewhere" to the hitbox stroke.
|
|
89
|
+
await page.mouse.move(10, 10);
|
|
90
|
+
await page.mouse.move(target.x, target.y);
|
|
91
|
+
await page.waitForTimeout(350);
|
|
92
|
+
const during = await opacities();
|
|
93
|
+
|
|
94
|
+
// Move far away — over an empty corner — so no node/edge is hovered.
|
|
95
|
+
await page.mouse.move(20, 20);
|
|
96
|
+
await page.waitForTimeout(250);
|
|
97
|
+
const after = await opacities();
|
|
98
|
+
|
|
99
|
+
await browser.close();
|
|
100
|
+
const baseOk = (o) => o >= 0.55; // online nodes at base ~1.0; allow 0.6 floor
|
|
101
|
+
const dimOk = (o) => o !== null && o < 0.4;
|
|
102
|
+
const results = {
|
|
103
|
+
beforeBaseAll: baseOk(before.alpha) && baseOk(before.beta) && baseOk(before.gamma) && baseOk(before.delta),
|
|
104
|
+
hoverKeepsEndpoints: baseOk(during.alpha) && baseOk(during.beta),
|
|
105
|
+
hoverDimsNonEndpoints: dimOk(during.gamma) && dimOk(during.delta),
|
|
106
|
+
releaseRestoresAll: baseOk(after.alpha) && baseOk(after.beta) && baseOk(after.gamma) && baseOk(after.delta),
|
|
107
|
+
};
|
|
108
|
+
const ok = Object.values(results).every(Boolean);
|
|
109
|
+
console.log(`${ok ? '✅' : '❌'} edge-endpoint highlight:`, JSON.stringify(results),
|
|
110
|
+
`\n before=`, before, `\n during=`, during, `\n after=`, after);
|
|
111
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/* Round 172 verification: edges fade-in alongside R9 staggered
|
|
2
|
+
* node reveal.
|
|
3
|
+
*
|
|
4
|
+
* Pre-R172 nodes used `anet-fade-in` with R9 stagger
|
|
5
|
+
* (animationDelay = tier×180 + nodeIdx×25 / nodeIdx×25 capped
|
|
6
|
+
* at 24) but edges appeared instantly. First-paint sequence:
|
|
7
|
+
* nodes ease in over ~600ms while edges already at full
|
|
8
|
+
* visibility — reveal looks inconsistent.
|
|
9
|
+
*
|
|
10
|
+
* R172 wraps each edge <g> in:
|
|
11
|
+
* className="anet-fade-in"
|
|
12
|
+
* style={{ animationDelay: '280ms + index*35ms (cap 20)' }}
|
|
13
|
+
* data-edge-group={link.key}
|
|
14
|
+
*
|
|
15
|
+
* Edges start fading in 280ms after first paint (after node
|
|
16
|
+
* stagger lands), staggered by 35ms intra-edge offset.
|
|
17
|
+
*
|
|
18
|
+
* Test:
|
|
19
|
+
* 1. Mock 4 sessions + 3 flows
|
|
20
|
+
* 2. Probe each edge <g[data-edge-group]>
|
|
21
|
+
* 3. Verify .anet-fade-in className present
|
|
22
|
+
* 4. Verify animation-delay attribute increments by 35ms
|
|
23
|
+
* starting from 280ms (clamped at idx=20)
|
|
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: 1500, 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) => ({
|
|
43
|
+
alias, status: 'working', 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'), mk('beta'), mk('gamma'), mk('delta'),
|
|
49
|
+
] } });
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const now = Date.now();
|
|
53
|
+
// 3 distinct flow links so we can probe stagger across multiple indices
|
|
54
|
+
const msgs = [
|
|
55
|
+
{ id: 'a', from_alias: 'alpha', to_alias: 'beta', content: 'hi', network_id: 'default', created_at: new Date(now - 5000).toISOString() },
|
|
56
|
+
{ id: 'b', from_alias: 'beta', to_alias: 'gamma', content: 'hi', network_id: 'default', created_at: new Date(now - 10000).toISOString() },
|
|
57
|
+
{ id: 'c', from_alias: 'gamma', to_alias: 'delta', content: 'hi', network_id: 'default', created_at: new Date(now - 15000).toISOString() },
|
|
58
|
+
];
|
|
59
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
|
|
60
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
61
|
+
|
|
62
|
+
const page = await ctx.newPage();
|
|
63
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
64
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
65
|
+
await page.waitForSelector('[data-edge-group]', { timeout: 10000 });
|
|
66
|
+
await page.waitForTimeout(400);
|
|
67
|
+
|
|
68
|
+
const probe = await page.evaluate(() => {
|
|
69
|
+
const edges = [...document.querySelectorAll('[data-edge-group]')];
|
|
70
|
+
return edges.map(e => ({
|
|
71
|
+
key: e.getAttribute('data-edge-group'),
|
|
72
|
+
hasFadeClass: (e.getAttribute('class') || '').includes('anet-fade-in'),
|
|
73
|
+
animationDelay: e.style.animationDelay || getComputedStyle(e).animationDelay,
|
|
74
|
+
}));
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
await browser.close();
|
|
78
|
+
|
|
79
|
+
if (probe.length < 3) {
|
|
80
|
+
console.log(`❌ wrong edge count: expected 3+, got ${probe.length}`, probe);
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Parse animationDelay (e.g. "280ms" or "0.28s") → number in ms
|
|
85
|
+
const parseDelay = (s) => {
|
|
86
|
+
if (!s) return null;
|
|
87
|
+
if (s.endsWith('ms')) return parseFloat(s);
|
|
88
|
+
if (s.endsWith('s')) return parseFloat(s) * 1000;
|
|
89
|
+
return parseFloat(s);
|
|
90
|
+
};
|
|
91
|
+
const delays = probe.map(p => parseDelay(p.animationDelay));
|
|
92
|
+
|
|
93
|
+
const results = {
|
|
94
|
+
three_plus_edges: probe.length >= 3,
|
|
95
|
+
all_have_fadeClass: probe.every(p => p.hasFadeClass),
|
|
96
|
+
all_have_delay: delays.every(d => d !== null && d >= 280),
|
|
97
|
+
first_edge_280ms: Math.abs(delays[0] - 280) < 0.5,
|
|
98
|
+
second_edge_315ms: Math.abs(delays[1] - 315) < 0.5,
|
|
99
|
+
third_edge_350ms: Math.abs(delays[2] - 350) < 0.5,
|
|
100
|
+
delays_strictly_increase: delays[0] < delays[1] && delays[1] < delays[2],
|
|
101
|
+
};
|
|
102
|
+
const ok = Object.values(results).every(Boolean);
|
|
103
|
+
console.log(`${ok ? '✅' : '❌'} edge fade-in:`, JSON.stringify(results),
|
|
104
|
+
`\n edges =`, probe,
|
|
105
|
+
`\n delays (ms) =`, delays);
|
|
106
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/* Round 50 verification: hovering a flow edge boosts that edge's own
|
|
2
|
+
* opacity 2× and thickens its stroke 1.4×, while every other edge dims
|
|
3
|
+
* to 0.35×. Complements R49 (which highlights the endpoint nodes) —
|
|
4
|
+
* together they make "this is the edge you're pointing at" unmistakable.
|
|
5
|
+
*
|
|
6
|
+
* Sessions: alpha, beta, gamma, delta. Messages: alpha→beta + gamma→delta.
|
|
7
|
+
* Hover the alpha→beta hitbox and read:
|
|
8
|
+
* - alpha→beta visible-path opacity boosts (~2× base)
|
|
9
|
+
* - alpha→beta visible-path strokeWidth = 1.4 × base (rounded by browser)
|
|
10
|
+
* - gamma→delta visible-path opacity dims (~0.35× base)
|
|
11
|
+
* - gamma→delta visible-path strokeWidth unchanged
|
|
12
|
+
*/
|
|
13
|
+
import { chromium } from 'playwright';
|
|
14
|
+
import { readFileSync } from 'node:fs';
|
|
15
|
+
|
|
16
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
17
|
+
const browser = await chromium.launch({ headless: true });
|
|
18
|
+
|
|
19
|
+
const ctx = await browser.newContext({ viewport: { width: 1280, height: 1500 } });
|
|
20
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
21
|
+
await ctx.addInitScript(() => {
|
|
22
|
+
try {
|
|
23
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
24
|
+
localStorage.setItem('anet-topo-layout', 'ring');
|
|
25
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
26
|
+
} catch {}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
30
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
31
|
+
const r = await route.fetch();
|
|
32
|
+
const b = await r.json();
|
|
33
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
34
|
+
const sessions = ['alpha', 'beta', 'gamma', 'delta'].map(a => ({
|
|
35
|
+
alias: a, status: 'idle', network_id: nid, project_dir: null,
|
|
36
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
37
|
+
}));
|
|
38
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
39
|
+
});
|
|
40
|
+
const now = new Date().toISOString();
|
|
41
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
|
|
42
|
+
{ from_alias: 'alpha', to_alias: 'beta', content: 'm', created_at: now },
|
|
43
|
+
{ from_alias: 'gamma', to_alias: 'delta', content: 'm', created_at: now },
|
|
44
|
+
] } }));
|
|
45
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
46
|
+
const page = await ctx.newPage();
|
|
47
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
48
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
49
|
+
await page.waitForTimeout(600);
|
|
50
|
+
|
|
51
|
+
// Read each flow edge's visible base path (the one with marker-end).
|
|
52
|
+
const readEdges = () => page.evaluate(() => {
|
|
53
|
+
const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
|
|
54
|
+
const rows = [];
|
|
55
|
+
for (const g of svg.querySelectorAll(':scope > g > g')) {
|
|
56
|
+
const t = g.querySelector('path[data-edge-hitbox] title');
|
|
57
|
+
if (!t) continue;
|
|
58
|
+
const route = (t.textContent || '').split('\n')[0];
|
|
59
|
+
const base = [...g.querySelectorAll(':scope > path')].find(
|
|
60
|
+
p => !p.hasAttribute('data-edge-hitbox') && p.hasAttribute('marker-end')
|
|
61
|
+
);
|
|
62
|
+
if (!base) continue;
|
|
63
|
+
rows.push({
|
|
64
|
+
route,
|
|
65
|
+
opacity: +base.getAttribute('opacity'),
|
|
66
|
+
strokeWidth: +base.getAttribute('stroke-width'),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return rows;
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const before = await readEdges();
|
|
73
|
+
|
|
74
|
+
// Move cursor to a point ON the alpha→beta hitbox curve.
|
|
75
|
+
const target = await page.evaluate(() => {
|
|
76
|
+
const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
|
|
77
|
+
for (const g of svg.querySelectorAll(':scope > g > g')) {
|
|
78
|
+
const t = g.querySelector('path[data-edge-hitbox] title');
|
|
79
|
+
if (t && /^alpha → beta/.test(t.textContent || '')) {
|
|
80
|
+
const hb = g.querySelector('path[data-edge-hitbox]');
|
|
81
|
+
const pt = hb.getPointAtLength(hb.getTotalLength() / 2);
|
|
82
|
+
const ctm = hb.getScreenCTM();
|
|
83
|
+
return { x: pt.x * ctm.a + pt.y * ctm.c + ctm.e, y: pt.x * ctm.b + pt.y * ctm.d + ctm.f };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
});
|
|
88
|
+
if (!target) { console.log('❌ no alpha→beta hitbox found'); process.exit(1); }
|
|
89
|
+
await page.mouse.move(10, 10);
|
|
90
|
+
await page.mouse.move(target.x, target.y);
|
|
91
|
+
await page.waitForTimeout(350);
|
|
92
|
+
const during = await readEdges();
|
|
93
|
+
|
|
94
|
+
await browser.close();
|
|
95
|
+
|
|
96
|
+
const find = (rows, r) => rows.find(e => e.route === r);
|
|
97
|
+
const b_ab = find(before, 'alpha → beta');
|
|
98
|
+
const b_gd = find(before, 'gamma → delta');
|
|
99
|
+
const a_ab = find(during, 'alpha → beta');
|
|
100
|
+
const a_gd = find(during, 'gamma → delta');
|
|
101
|
+
|
|
102
|
+
const results = {
|
|
103
|
+
baselineMatches: b_ab && b_gd && Math.abs(b_ab.opacity - b_gd.opacity) < 0.001 && b_ab.strokeWidth === b_gd.strokeWidth,
|
|
104
|
+
hoveredEdgeBrightens: a_ab && a_ab.opacity > b_ab.opacity * 1.5,
|
|
105
|
+
hoveredEdgeThickens: a_ab && a_ab.strokeWidth > b_ab.strokeWidth * 1.2,
|
|
106
|
+
otherEdgeDims: a_gd && a_gd.opacity < b_gd.opacity * 0.6,
|
|
107
|
+
otherEdgeStaysThin: a_gd && a_gd.strokeWidth === b_gd.strokeWidth,
|
|
108
|
+
};
|
|
109
|
+
const ok = Object.values(results).every(Boolean);
|
|
110
|
+
console.log(`${ok ? '✅' : '❌'} edge focus:`, JSON.stringify(results),
|
|
111
|
+
`\n before α→β=${JSON.stringify(b_ab)} γ→δ=${JSON.stringify(b_gd)}`,
|
|
112
|
+
`\n during α→β=${JSON.stringify(a_ab)} γ→δ=${JSON.stringify(a_gd)}`);
|
|
113
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -45,13 +45,21 @@ await page.waitForTimeout(600);
|
|
|
45
45
|
|
|
46
46
|
const edgeOpacities = () => page.evaluate(() => {
|
|
47
47
|
const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
|
|
48
|
-
//
|
|
49
|
-
//
|
|
48
|
+
// R48 reordered the children of each flow-link <g>:
|
|
49
|
+
// path[data-edge-hitbox] <-- transparent hover surface, carries <title>
|
|
50
|
+
// path[marker-end] <-- the visible base edge with opacity
|
|
51
|
+
// path[id^=flow-path-] <-- the dashed runway
|
|
52
|
+
// Read the route from the hitbox's <title>, but the opacity from the
|
|
53
|
+
// visible base path. Picking by marker-end keeps the selector resilient
|
|
54
|
+
// to future siblings.
|
|
50
55
|
return [...svg.querySelectorAll(':scope > g > g')]
|
|
51
56
|
.map(g => {
|
|
52
|
-
const
|
|
53
|
-
const title =
|
|
54
|
-
|
|
57
|
+
const hitbox = g.querySelector('path[data-edge-hitbox]');
|
|
58
|
+
const title = hitbox?.querySelector('title');
|
|
59
|
+
const base = [...g.querySelectorAll(':scope > path')].find(
|
|
60
|
+
p => !p.hasAttribute('data-edge-hitbox') && p.hasAttribute('marker-end')
|
|
61
|
+
);
|
|
62
|
+
if (!title || !base) return null;
|
|
55
63
|
return { route: title.textContent.split('\n')[0], opacity: +base.getAttribute('opacity') };
|
|
56
64
|
})
|
|
57
65
|
.filter(Boolean);
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/* Round 150 verification: R119 edge filter pill extends the hot-
|
|
2
|
+
* lane convention. When the pinned edge has count >= 10, the
|
|
3
|
+
* count tspan inside the pill flips to amber + 700 weight, and
|
|
4
|
+
* the tooltip grows a "hot lane · ≥ 10" marker.
|
|
5
|
+
*
|
|
6
|
+
* 4th hot-lane surface now active:
|
|
7
|
+
* R126 canvas badge → amber stroke when count >= 10
|
|
8
|
+
* R127 recent-row count → amber + bold tspan
|
|
9
|
+
* R129 panel header tail → "· N hot" amber suffix
|
|
10
|
+
* R150 edge filter pill → amber count tspan + tooltip marker
|
|
11
|
+
*
|
|
12
|
+
* All four use the same hotStroke palette (#d97706 light / #fbbf24
|
|
13
|
+
* dark) so the eye instantly recognises hot signal across
|
|
14
|
+
* surfaces.
|
|
15
|
+
*
|
|
16
|
+
* Two test states:
|
|
17
|
+
* - Pin a hot edge (count >= 10):
|
|
18
|
+
* - data-active-filter-edge-hot="true"
|
|
19
|
+
* - data-active-filter-edge-count-hot tspan present
|
|
20
|
+
* - tspan color = amber, weight = 700
|
|
21
|
+
* - title contains "hot lane · ≥ 10"
|
|
22
|
+
* - Pin a warm edge (count < 10):
|
|
23
|
+
* - data-active-filter-edge-hot="false"
|
|
24
|
+
* - data-active-filter-edge-count tspan present (non-hot)
|
|
25
|
+
* - title does NOT contain "hot lane"
|
|
26
|
+
*/
|
|
27
|
+
import { chromium } from 'playwright';
|
|
28
|
+
import { readFileSync } from 'node:fs';
|
|
29
|
+
|
|
30
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
31
|
+
const browser = await chromium.launch({ headless: true });
|
|
32
|
+
|
|
33
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
|
|
34
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
35
|
+
await ctx.addInitScript(() => {
|
|
36
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
40
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
41
|
+
const r = await route.fetch();
|
|
42
|
+
const b = await r.json();
|
|
43
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
44
|
+
const mk = (alias, status) => ({
|
|
45
|
+
alias, status, model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
46
|
+
network_id: nid, project_dir: null,
|
|
47
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
48
|
+
});
|
|
49
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
50
|
+
mk('alpha', 'working'), mk('beta', 'working'), mk('gamma', 'idle'),
|
|
51
|
+
] } });
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const now = Date.now();
|
|
55
|
+
const mkMsg = (id, from, to, ageMs) => ({
|
|
56
|
+
id, from_alias: from, to_alias: to, content: 'hi',
|
|
57
|
+
network_id: 'default', created_at: new Date(now - ageMs).toISOString(),
|
|
58
|
+
});
|
|
59
|
+
const msgs = [];
|
|
60
|
+
// 12 alpha→beta = HOT (count >= 10)
|
|
61
|
+
for (let i = 0; i < 12; i++) msgs.push(mkMsg(`a${i}`, 'alpha', 'beta', 20000 + i * 500));
|
|
62
|
+
// 4 beta→gamma = warm
|
|
63
|
+
for (let i = 0; i < 4; i++) msgs.push(mkMsg(`b${i}`, 'beta', 'gamma', 30000 + i * 500));
|
|
64
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
|
|
65
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
66
|
+
|
|
67
|
+
const page = await ctx.newPage();
|
|
68
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
69
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
|
|
70
|
+
await page.waitForSelector('[data-recent-row="alpha->beta"]', { timeout: 10000 });
|
|
71
|
+
await page.waitForTimeout(400);
|
|
72
|
+
|
|
73
|
+
const inspectPill = () => page.evaluate(() => {
|
|
74
|
+
const pill = document.querySelector('[data-active-filter="edge"]');
|
|
75
|
+
if (!pill) return null;
|
|
76
|
+
const hotTspan = pill.querySelector('[data-active-filter-edge-count-hot]');
|
|
77
|
+
const warmTspan = pill.querySelector('[data-active-filter-edge-count]');
|
|
78
|
+
return {
|
|
79
|
+
hot: pill.getAttribute('data-active-filter-edge-hot'),
|
|
80
|
+
title: pill.getAttribute('title'),
|
|
81
|
+
hotTspanText: hotTspan?.textContent?.trim(),
|
|
82
|
+
hotTspanStyle: hotTspan?.getAttribute('style'),
|
|
83
|
+
warmTspanText: warmTspan?.textContent?.trim(),
|
|
84
|
+
matchCount: pill.getAttribute('data-filter-match-count'),
|
|
85
|
+
};
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Pin hot edge alpha→beta (12 msgs)
|
|
89
|
+
await page.locator('[data-recent-row="alpha->beta"]').click();
|
|
90
|
+
await page.waitForTimeout(250);
|
|
91
|
+
const hot = await inspectPill();
|
|
92
|
+
|
|
93
|
+
// Unpin → pin warm edge beta→gamma (4 msgs)
|
|
94
|
+
await page.keyboard.press('Escape');
|
|
95
|
+
await page.waitForTimeout(200);
|
|
96
|
+
await page.locator('[data-recent-row="beta->gamma"]').click();
|
|
97
|
+
await page.waitForTimeout(250);
|
|
98
|
+
const warm = await inspectPill();
|
|
99
|
+
|
|
100
|
+
await browser.close();
|
|
101
|
+
|
|
102
|
+
// cyber theme amber #fbbf24 = rgb(251, 191, 36)
|
|
103
|
+
const amberInStyle = (s) =>
|
|
104
|
+
!!s && (/rgba?\(251,\s*191,\s*36/i.test(s) || /#fbbf24/i.test(s));
|
|
105
|
+
|
|
106
|
+
const results = {
|
|
107
|
+
// Hot edge pinned (alpha→beta, 12 msgs)
|
|
108
|
+
hot_pillPresent: hot !== null,
|
|
109
|
+
hot_attrTrue: hot?.hot === 'true',
|
|
110
|
+
hot_matchCount12: hot?.matchCount === '12',
|
|
111
|
+
hot_tspanHasCount: hot?.hotTspanText?.includes('12') === true,
|
|
112
|
+
hot_tspanAmberColor: amberInStyle(hot?.hotTspanStyle || ''),
|
|
113
|
+
hot_tspanWeight700: (hot?.hotTspanStyle || '').includes('font-weight: 700'),
|
|
114
|
+
hot_titleHotMarker: (hot?.title || '').includes('hot lane'),
|
|
115
|
+
hot_noWarmTspan: hot?.warmTspanText === undefined,
|
|
116
|
+
|
|
117
|
+
// Warm edge pinned (beta→gamma, 4 msgs)
|
|
118
|
+
warm_pillPresent: warm !== null,
|
|
119
|
+
warm_attrFalse: warm?.hot === 'false',
|
|
120
|
+
warm_matchCount4: warm?.matchCount === '4',
|
|
121
|
+
warm_tspanHasCount: warm?.warmTspanText?.includes('4') === true,
|
|
122
|
+
warm_noHotTspan: warm?.hotTspanText === undefined,
|
|
123
|
+
warm_titleNoHotMarker: !(warm?.title || '').includes('hot lane'),
|
|
124
|
+
};
|
|
125
|
+
const ok = Object.values(results).every(Boolean);
|
|
126
|
+
console.log(`${ok ? '✅' : '❌'} edge pill hot:`, JSON.stringify(results),
|
|
127
|
+
`\n hot=`, hot,
|
|
128
|
+
`\n warm=`, warm);
|
|
129
|
+
process.exit(ok ? 0 : 1);
|