@sleep2agi/agent-network-dashboard 0.4.6-preview.99 → 0.5.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/0aauz~36q5n2a.css +2 -0
- package/.next/static/chunks/0f~0kgy2pycyc.js +1 -0
- package/.next/static/chunks/0l_~q07bhpkcx.js +7 -0
- package/.next/static/chunks/1175-dp18a1e_.js +1 -0
- package/.next/static/chunks/1552j0415oeal.js +4 -0
- 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 +41 -1
- package/app/components/HelpOverlay.tsx +55 -0
- package/app/components/ServersDrawer.tsx +181 -5
- package/app/components/TopoGraph.tsx +5514 -336
- package/app/globals.css +93 -6
- 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-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-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-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-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-fadein-test.mjs +106 -0
- 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-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-filter-pill-aliases-test.mjs +121 -0
- package/scripts/topo-filter-pill-count-tabular-test.mjs +135 -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-label-a11y-test.mjs +113 -0
- package/scripts/topo-group-label-count-inherit-test.mjs +99 -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-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-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-march-busyness-test.mjs +155 -0
- 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-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-center-crossfade-test.mjs +124 -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-working-count-test.mjs +99 -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-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-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-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-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-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-prefix-opacity-test.mjs +107 -0
- package/scripts/topo-prepublish-guard-test.mjs +95 -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-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-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-tint-test.mjs +110 -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-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-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/03w4rgx_vt8ec.js +0 -1
- package/.next/static/chunks/072so8rfx2tt-.css +0 -2
- package/.next/static/chunks/0o.k-.0kl4bz5.js +0 -2
- package/.next/static/chunks/121d8yvfl8l_..js +0 -7
- package/.next/static/chunks/148o1e4ijgwp1.js +0 -1
- package/scripts/topo-arrival-ping-test.mjs +0 -87
- package/scripts/topo-dispatch-pulse-test.mjs +0 -83
- /package/.next/static/{Dhky2WTh8mvJhw56taHVs → 424zX3i80AJ_wHF_otKuP}/_buildManifest.js +0 -0
- /package/.next/static/{Dhky2WTh8mvJhw56taHVs → 424zX3i80AJ_wHF_otKuP}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{Dhky2WTh8mvJhw56taHVs → 424zX3i80AJ_wHF_otKuP}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/* Round 124 verification: pin-intersection summary chip.
|
|
2
|
+
*
|
|
3
|
+
* The four filter pills (R64 status / R63 group / R89 vendor /
|
|
4
|
+
* R119 edge) each show their own dim's match count in isolation.
|
|
5
|
+
* But the node-opacity chain (lines 2723-2741 of TopoGraph.tsx)
|
|
6
|
+
* AND-composes all four pin dimensions — every miss dims the node.
|
|
7
|
+
* So two active pins produce an intersection ≤ either individual
|
|
8
|
+
* count, and the existing pills don't surface that.
|
|
9
|
+
*
|
|
10
|
+
* R124 adds a small "match: N pins · K" chip after the last pill
|
|
11
|
+
* when ≥ 2 pin dims are active. K = nodes that satisfy ALL active
|
|
12
|
+
* pins. Single pin keeps its old pill story (chip hidden).
|
|
13
|
+
*
|
|
14
|
+
* Fleet:
|
|
15
|
+
* alpha (working, vendor=A, group=agents)
|
|
16
|
+
* beta (working, vendor=O, group=agents)
|
|
17
|
+
* gamma (idle, vendor=A, group=agents)
|
|
18
|
+
* delta (working, vendor=A, group=infra)
|
|
19
|
+
*
|
|
20
|
+
* Test path:
|
|
21
|
+
* 1. No pin → chip hidden
|
|
22
|
+
* 2. Pin status=working → chip hidden (1 pin)
|
|
23
|
+
* 3. + Pin vendor=A → chip "2 pins · 2"
|
|
24
|
+
* (working AND vendor=A: alpha, delta)
|
|
25
|
+
* 4. + Pin group=agents → chip "3 pins · 1"
|
|
26
|
+
* (working AND A AND agents: alpha only)
|
|
27
|
+
* 5. Esc → chip hidden
|
|
28
|
+
*/
|
|
29
|
+
import { chromium } from 'playwright';
|
|
30
|
+
import { readFileSync } from 'node:fs';
|
|
31
|
+
|
|
32
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
33
|
+
const browser = await chromium.launch({ headless: true });
|
|
34
|
+
|
|
35
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1100 } });
|
|
36
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
37
|
+
await ctx.addInitScript(() => {
|
|
38
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
42
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
43
|
+
const r = await route.fetch();
|
|
44
|
+
const b = await r.json();
|
|
45
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
46
|
+
const mk = (alias, status, model) => ({
|
|
47
|
+
alias, status, model, runtime: 'cli-claude-code',
|
|
48
|
+
network_id: nid, project_dir: alias.startsWith('infra-') ? '/srv/infra' : '/srv/agents',
|
|
49
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
50
|
+
});
|
|
51
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
52
|
+
mk('agents-alpha', 'working', 'claude-opus-4'),
|
|
53
|
+
mk('agents-beta', 'working', 'gpt-4o'),
|
|
54
|
+
mk('agents-gamma', 'idle', 'claude-sonnet-4'),
|
|
55
|
+
mk('infra-delta', 'working', 'claude-haiku-4'),
|
|
56
|
+
] } });
|
|
57
|
+
});
|
|
58
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
59
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
60
|
+
|
|
61
|
+
const page = await ctx.newPage();
|
|
62
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
63
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
64
|
+
await page.waitForTimeout(400);
|
|
65
|
+
|
|
66
|
+
const readChip = () => page.evaluate(() => {
|
|
67
|
+
const el = document.querySelector('[data-pin-intersection]');
|
|
68
|
+
if (!el) return null;
|
|
69
|
+
return {
|
|
70
|
+
dimCount: parseInt(el.getAttribute('data-pin-dim-count') || '0', 10),
|
|
71
|
+
matchCount: parseInt(el.getAttribute('data-pin-intersection-count') || '0', 10),
|
|
72
|
+
aliases: (el.getAttribute('data-pin-intersection-aliases') || '').split(',').filter(Boolean),
|
|
73
|
+
text: el.textContent?.trim(),
|
|
74
|
+
};
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// State 1 — no pins
|
|
78
|
+
const s1 = await readChip();
|
|
79
|
+
|
|
80
|
+
// State 2 — pin status=working via pressure-bar segment
|
|
81
|
+
await page.locator('[data-pressure-seg="working"]').click();
|
|
82
|
+
await page.waitForTimeout(200);
|
|
83
|
+
const s2 = await readChip();
|
|
84
|
+
|
|
85
|
+
// State 3 — + pin vendor=A
|
|
86
|
+
await page.locator('[data-vendor-letter="A"]').click();
|
|
87
|
+
await page.waitForTimeout(200);
|
|
88
|
+
const s3 = await readChip();
|
|
89
|
+
|
|
90
|
+
// State 4 — + pin group=agents via the R69 CustomEvent contract.
|
|
91
|
+
// Direct dispatch is mode-independent (group-label hit-rects only
|
|
92
|
+
// render in grid layout; default is ring). The handler at line 945
|
|
93
|
+
// of TopoGraph.tsx accepts { kind: 'group', value: <key> }.
|
|
94
|
+
await page.evaluate(() => {
|
|
95
|
+
window.dispatchEvent(new CustomEvent('anet:topo-pin', { detail: { kind: 'group', value: 'agents' } }));
|
|
96
|
+
});
|
|
97
|
+
await page.waitForTimeout(200);
|
|
98
|
+
const s4 = await readChip();
|
|
99
|
+
|
|
100
|
+
// State 5 — Esc clears all pins
|
|
101
|
+
await page.keyboard.press('Escape');
|
|
102
|
+
await page.waitForTimeout(200);
|
|
103
|
+
const s5 = await readChip();
|
|
104
|
+
|
|
105
|
+
await browser.close();
|
|
106
|
+
|
|
107
|
+
const results = {
|
|
108
|
+
s1_noPins_chipHidden: s1 === null,
|
|
109
|
+
s2_onePin_chipHidden: s2 === null,
|
|
110
|
+
s3_twoPins_chipShown: !!s3 && s3.dimCount === 2,
|
|
111
|
+
s3_intersection_2: !!s3 && s3.matchCount === 2,
|
|
112
|
+
s3_aliases_alphaDelta: !!s3 && s3.aliases.includes('agents-alpha') && s3.aliases.includes('infra-delta'),
|
|
113
|
+
s4_threePins_chipShown: !!s4 && s4.dimCount === 3,
|
|
114
|
+
s4_intersection_1: !!s4 && s4.matchCount === 1,
|
|
115
|
+
s4_aliases_alphaOnly: !!s4 && s4.aliases.length === 1 && s4.aliases[0] === 'agents-alpha',
|
|
116
|
+
s5_escClears_chipHidden: s5 === null,
|
|
117
|
+
};
|
|
118
|
+
const ok = Object.values(results).every(Boolean);
|
|
119
|
+
console.log(`${ok ? '✅' : '❌'} pin-intersection chip:`, JSON.stringify(results),
|
|
120
|
+
`\n s1=`, s1, `\n s2=`, s2, `\n s3=`, s3, `\n s4=`, s4, `\n s5=`, s5);
|
|
121
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/* Round 237 verification: pin-intersection chip's ⚠ warning glyph
|
|
2
|
+
* always-mounts with opacity gated on isEmpty, instead of
|
|
3
|
+
* conditional render on isEmpty. Closes the snap-mount + layout-
|
|
4
|
+
* shift jank that survived R236's color easing.
|
|
5
|
+
*
|
|
6
|
+
* Test scope:
|
|
7
|
+
* - non-empty state: glyph in DOM, data-pin-intersection-warning
|
|
8
|
+
* ='false', opacity=0, transition includes 'opacity 200ms'
|
|
9
|
+
* - empty state: glyph in DOM, data-pin-intersection-warning
|
|
10
|
+
* ='true', opacity=1, transition still present
|
|
11
|
+
* - chip width: stable across the crossing (no layout-shift)
|
|
12
|
+
*
|
|
13
|
+
* Reuses the R236 scenario tuning — alpha is the only working
|
|
14
|
+
* anthropic, gpt sessions all idle; pin working+A (non-empty)
|
|
15
|
+
* then re-pin vendor to O (empty).
|
|
16
|
+
*/
|
|
17
|
+
import { chromium } from 'playwright';
|
|
18
|
+
import { readFileSync } from 'node:fs';
|
|
19
|
+
|
|
20
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
21
|
+
const browser = await chromium.launch({ headless: true });
|
|
22
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
23
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
24
|
+
await ctx.addInitScript(() => {
|
|
25
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
26
|
+
});
|
|
27
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
28
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
29
|
+
const r = await route.fetch();
|
|
30
|
+
const b = await r.json();
|
|
31
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
32
|
+
const mk = (alias, model, status) => ({
|
|
33
|
+
alias, status, model, runtime: 'claude-code-cli',
|
|
34
|
+
network_id: nid, project_dir: null,
|
|
35
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
36
|
+
});
|
|
37
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
38
|
+
mk('alpha', 'claude-opus-4', 'working'),
|
|
39
|
+
mk('beta', 'claude-opus-4', 'idle'),
|
|
40
|
+
mk('gamma', 'gpt-4', 'idle'),
|
|
41
|
+
mk('delta', 'gpt-4', 'idle'),
|
|
42
|
+
] } });
|
|
43
|
+
});
|
|
44
|
+
await ctx.route('**/api/hub/messages*', (r2) => r2.fulfill({ json: { messages: [] } }));
|
|
45
|
+
await ctx.route('**/api/hub/tasks*', (r2) => r2.fulfill({ json: { tasks: [] } }));
|
|
46
|
+
|
|
47
|
+
const page = await ctx.newPage();
|
|
48
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
49
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
50
|
+
|
|
51
|
+
await page.locator('[data-pressure-seg="working"]').first().click();
|
|
52
|
+
await page.locator('[data-vendor-letter="A"]').first().click();
|
|
53
|
+
await page.waitForSelector('[data-pin-intersection-warning]', { timeout: 5000, state: 'attached' });
|
|
54
|
+
await page.waitForTimeout(300);
|
|
55
|
+
|
|
56
|
+
const probe = (page) => page.evaluate(() => {
|
|
57
|
+
const glyph = document.querySelector('[data-pin-intersection-warning]');
|
|
58
|
+
const chip = document.querySelector('[data-pin-intersection]');
|
|
59
|
+
if (!glyph || !chip) return null;
|
|
60
|
+
return {
|
|
61
|
+
present: true,
|
|
62
|
+
warning: glyph.getAttribute('data-pin-intersection-warning'),
|
|
63
|
+
opacity: parseFloat(glyph.style.opacity || '1'),
|
|
64
|
+
transition: glyph.style.transition,
|
|
65
|
+
text: glyph.textContent,
|
|
66
|
+
chipWidthPx: Math.round(chip.getBoundingClientRect().width * 100) / 100,
|
|
67
|
+
chipEmpty: chip.getAttribute('data-pin-intersection-empty'),
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const nonEmpty = await probe(page);
|
|
72
|
+
|
|
73
|
+
// Flip vendor pin to 'O' — working ∩ openai = 0 (gpt all idle) → empty
|
|
74
|
+
await page.locator('[data-vendor-letter="O"]').first().click();
|
|
75
|
+
await page.waitForTimeout(300);
|
|
76
|
+
const empty = await probe(page);
|
|
77
|
+
|
|
78
|
+
await browser.close();
|
|
79
|
+
|
|
80
|
+
const widthDelta = nonEmpty && empty
|
|
81
|
+
? Math.abs(nonEmpty.chipWidthPx - empty.chipWidthPx)
|
|
82
|
+
: Infinity;
|
|
83
|
+
|
|
84
|
+
const results = {
|
|
85
|
+
nonempty_in_dom: nonEmpty?.present === true,
|
|
86
|
+
nonempty_warning_false: nonEmpty?.warning === 'false',
|
|
87
|
+
nonempty_opacity_0: nonEmpty?.opacity === 0,
|
|
88
|
+
nonempty_has_transition: /opacity\s+200ms/.test(nonEmpty?.transition || ''),
|
|
89
|
+
nonempty_text_is_warning: nonEmpty?.text === '⚠',
|
|
90
|
+
nonempty_chip_empty_false: nonEmpty?.chipEmpty === 'false',
|
|
91
|
+
|
|
92
|
+
empty_in_dom: empty?.present === true,
|
|
93
|
+
empty_warning_true: empty?.warning === 'true',
|
|
94
|
+
empty_opacity_1: empty?.opacity === 1,
|
|
95
|
+
empty_has_transition: /opacity\s+200ms/.test(empty?.transition || ''),
|
|
96
|
+
empty_chip_empty_true: empty?.chipEmpty === 'true',
|
|
97
|
+
|
|
98
|
+
// Chip width stable across the crossing (no layout-shift jank).
|
|
99
|
+
// 1px tolerance for fractional rounding only.
|
|
100
|
+
chip_width_stable: widthDelta <= 1,
|
|
101
|
+
};
|
|
102
|
+
const ok = Object.values(results).every(Boolean);
|
|
103
|
+
console.log(`${ok ? '✅' : '❌'} pin-intersection warning crossfade:`, JSON.stringify(results),
|
|
104
|
+
'\n nonEmpty:', nonEmpty,
|
|
105
|
+
'\n empty: ', empty,
|
|
106
|
+
'\n widthDelta:', widthDelta);
|
|
107
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/* Round 180 verification: working / online / vendor-letter chips
|
|
2
|
+
* pick up box-shadow transition on pin/unpin.
|
|
3
|
+
*
|
|
4
|
+
* Pre-R180 the inset-ring pin-mirror style snapped instantly when
|
|
5
|
+
* the user pinned or unpinned a filter via these three chip
|
|
6
|
+
* surfaces. R165 had added the transition to pressure-bar
|
|
7
|
+
* segments only — R180 closes the smooth-pin-mirror family
|
|
8
|
+
* across all chip-row pinnable surfaces.
|
|
9
|
+
*
|
|
10
|
+
* Test:
|
|
11
|
+
* 1. Mock 3 working sessions across 2 vendors (claude + 书生)
|
|
12
|
+
* 2. Probe inline transition on working / online / vendor chips
|
|
13
|
+
* 3. All three should have 'box-shadow 150ms ease-out' in their
|
|
14
|
+
* inline transition style
|
|
15
|
+
*/
|
|
16
|
+
import { chromium } from 'playwright';
|
|
17
|
+
import { readFileSync } from 'node:fs';
|
|
18
|
+
|
|
19
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
20
|
+
const browser = await chromium.launch({ headless: true });
|
|
21
|
+
|
|
22
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
23
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
24
|
+
await ctx.addInitScript(() => {
|
|
25
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
29
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
30
|
+
const r = await route.fetch();
|
|
31
|
+
const b = await r.json();
|
|
32
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
33
|
+
// Two distinct vendor families so vendor letter chip shows up
|
|
34
|
+
// (vendorDist.length > 1 requirement).
|
|
35
|
+
const mk = (alias, model) => ({
|
|
36
|
+
alias, status: 'working', model, runtime: 'cli-claude-code',
|
|
37
|
+
network_id: nid, project_dir: null,
|
|
38
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
39
|
+
});
|
|
40
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
41
|
+
mk('alpha', 'claude-opus-4'),
|
|
42
|
+
mk('beta', 'claude-opus-4'),
|
|
43
|
+
mk('gamma', 'intern-s1-mini'),
|
|
44
|
+
] } });
|
|
45
|
+
});
|
|
46
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
47
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
48
|
+
|
|
49
|
+
const page = await ctx.newPage();
|
|
50
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
51
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
|
|
52
|
+
await page.waitForSelector('[data-working-chip]', { timeout: 10000 });
|
|
53
|
+
await page.waitForSelector('[data-vendor-letter]', { timeout: 10000 });
|
|
54
|
+
await page.waitForTimeout(400);
|
|
55
|
+
|
|
56
|
+
const probe = async () => page.evaluate(() => {
|
|
57
|
+
const grab = (sel) => {
|
|
58
|
+
const el = document.querySelector(sel);
|
|
59
|
+
return el ? {
|
|
60
|
+
transition: el.style.transition || getComputedStyle(el).transition || '',
|
|
61
|
+
boxShadow: el.style.boxShadow || getComputedStyle(el).boxShadow || '',
|
|
62
|
+
} : null;
|
|
63
|
+
};
|
|
64
|
+
return {
|
|
65
|
+
working: grab('[data-working-chip]'),
|
|
66
|
+
online: grab('[data-online-chip-clickable]'),
|
|
67
|
+
vendor: grab('[data-vendor-letter]'),
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const idle = await probe();
|
|
72
|
+
|
|
73
|
+
// Click the working chip to pin → boxShadow should appear
|
|
74
|
+
await page.locator('[data-working-chip]').click();
|
|
75
|
+
await page.waitForTimeout(250);
|
|
76
|
+
const pinnedWorking = await probe();
|
|
77
|
+
|
|
78
|
+
await browser.close();
|
|
79
|
+
|
|
80
|
+
const hasTransition = (s) =>
|
|
81
|
+
(s?.transition || '').includes('box-shadow 150ms') ||
|
|
82
|
+
/box-shadow\s+0\.15s|box-shadow\s+150ms/.test(s?.transition || '');
|
|
83
|
+
|
|
84
|
+
const results = {
|
|
85
|
+
working_found: idle.working !== null,
|
|
86
|
+
online_found: idle.online !== null,
|
|
87
|
+
vendor_found: idle.vendor !== null,
|
|
88
|
+
working_has_bs_transition: hasTransition(idle.working),
|
|
89
|
+
online_has_bs_transition: hasTransition(idle.online),
|
|
90
|
+
vendor_has_bs_transition: hasTransition(idle.vendor),
|
|
91
|
+
// Pre-pin: no inset rings on working chip
|
|
92
|
+
working_idle_no_inset: !(idle.working?.boxShadow || '').includes('inset'),
|
|
93
|
+
// After pin: inset rings appear, transition still set
|
|
94
|
+
working_pinned_has_inset: (pinnedWorking.working?.boxShadow || '').includes('inset'),
|
|
95
|
+
working_pinned_keeps_transition: hasTransition(pinnedWorking.working),
|
|
96
|
+
};
|
|
97
|
+
const ok = Object.values(results).every(Boolean);
|
|
98
|
+
console.log(`${ok ? '✅' : '❌'} pin-mirror transition:`, JSON.stringify(results),
|
|
99
|
+
`\n idle =`, idle,
|
|
100
|
+
`\n pinned =`, pinnedWorking);
|
|
101
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/* Round 335 verification: filter pin prefix labels (`filter:` /
|
|
2
|
+
* `match:`) gain opacity-70 so the prefix recedes and the value
|
|
3
|
+
* text reads as primary content. Sibling to R333 vendor count
|
|
4
|
+
* suffix subordinate-lift family — recurring pattern: small
|
|
5
|
+
* "label" spans demote to opacity-70 while value text stays
|
|
6
|
+
* full-opacity, creating clear label-vs-value hierarchy inside
|
|
7
|
+
* the same chip.
|
|
8
|
+
*
|
|
9
|
+
* Five prefix surfaces share the `className="hidden sm:inline"`
|
|
10
|
+
* pattern (R335 replace_all):
|
|
11
|
+
* - status pin pill [data-filter-prefix]
|
|
12
|
+
* - group pin pill [data-filter-prefix]
|
|
13
|
+
* - vendor pin pill [data-filter-prefix]
|
|
14
|
+
* - edge pin pill [data-filter-prefix]
|
|
15
|
+
* - intersection chip [data-pin-intersection-prefix]
|
|
16
|
+
*
|
|
17
|
+
* Contract:
|
|
18
|
+
* - Activate a status pin (click working chip) so the status
|
|
19
|
+
* filter pin pill mounts with its `filter:` prefix visible.
|
|
20
|
+
* - The [data-filter-prefix] span has className containing
|
|
21
|
+
* 'opacity-70'.
|
|
22
|
+
* - Computed opacity reads 0.7.
|
|
23
|
+
* - R333 vendor count suffix still gray-400 + tabular.
|
|
24
|
+
* - R317/R318/R294 chrome + pulse regressions intact.
|
|
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
|
+
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
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
37
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
38
|
+
const r = await route.fetch();
|
|
39
|
+
const b = await r.json();
|
|
40
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
41
|
+
const mk = (alias, model) => ({
|
|
42
|
+
alias, status: 'working', model, runtime: 'claude-code-cli',
|
|
43
|
+
network_id: nid, project_dir: null,
|
|
44
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
45
|
+
});
|
|
46
|
+
// 3 vendor types so the vendor letter chip row also renders (for
|
|
47
|
+
// R333 vendor count suffix regression check).
|
|
48
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
49
|
+
mk('alpha', 'claude-opus-4'),
|
|
50
|
+
mk('beta', 'gpt-4o'),
|
|
51
|
+
mk('gamma', 'internlm/internlm2'),
|
|
52
|
+
] } });
|
|
53
|
+
});
|
|
54
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
55
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
56
|
+
|
|
57
|
+
const page = await ctx.newPage();
|
|
58
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
59
|
+
await page.waitForSelector('[data-working-chip]', { timeout: 15000 });
|
|
60
|
+
await page.waitForTimeout(200);
|
|
61
|
+
|
|
62
|
+
// Pin the working status to mount the status filter pill (which
|
|
63
|
+
// contains the `filter:` prefix).
|
|
64
|
+
await page.click('[data-working-chip]', { delay: 50 }).catch(() => {});
|
|
65
|
+
await page.waitForTimeout(400);
|
|
66
|
+
|
|
67
|
+
const probe = await page.evaluate(() => {
|
|
68
|
+
const prefixes = Array.from(document.querySelectorAll('[data-filter-prefix]'));
|
|
69
|
+
const intPrefix = document.querySelector('[data-pin-intersection-prefix]');
|
|
70
|
+
const vendorSuffix = document.querySelector('[data-vendor-letter-count-suffix]');
|
|
71
|
+
return {
|
|
72
|
+
prefixCount: prefixes.length,
|
|
73
|
+
prefixClasses: prefixes.map(p => p.className),
|
|
74
|
+
prefixOpacities: prefixes.map(p => getComputedStyle(p).opacity),
|
|
75
|
+
intPrefixClass: intPrefix?.className ?? null,
|
|
76
|
+
intPrefixOpacity: intPrefix ? getComputedStyle(intPrefix).opacity : null,
|
|
77
|
+
vendorSuffixClass: vendorSuffix?.className ?? '',
|
|
78
|
+
vendorSuffixTab: vendorSuffix ? getComputedStyle(vendorSuffix).fontVariantNumeric : null,
|
|
79
|
+
layoutInactiveCls: document.querySelector('[data-topo-chrome-layout="grid"]')?.className ?? '',
|
|
80
|
+
layoutActiveCls: document.querySelector('[data-topo-chrome-layout="ring"]')?.className ?? '',
|
|
81
|
+
pulseCount: document.querySelectorAll('[data-pulse-wrapper]').length,
|
|
82
|
+
};
|
|
83
|
+
});
|
|
84
|
+
await browser.close();
|
|
85
|
+
|
|
86
|
+
const hasOpacity70 = (cls) => /opacity-70/.test(cls || '');
|
|
87
|
+
const opacityClose = (val) => Math.abs(parseFloat(val) - 0.7) < 0.01;
|
|
88
|
+
|
|
89
|
+
const results = {
|
|
90
|
+
prefix_at_least_one: probe.prefixCount >= 1,
|
|
91
|
+
all_prefix_have_opacity70: probe.prefixClasses.every(hasOpacity70),
|
|
92
|
+
all_prefix_computed_0_7: probe.prefixOpacities.every(opacityClose),
|
|
93
|
+
// Pin-intersection chip only mounts when ≥2 dims pinned, which we
|
|
94
|
+
// didn't trigger here (only one click). Accept null OR opacity-70.
|
|
95
|
+
intersection_prefix_ok: probe.intPrefixClass === null || hasOpacity70(probe.intPrefixClass),
|
|
96
|
+
// R333 vendor count suffix regression.
|
|
97
|
+
r333_vendor_suffix_gray400: probe.vendorSuffixClass.includes('text-gray-400'),
|
|
98
|
+
r333_vendor_suffix_tabular: /tabular-nums/.test(probe.vendorSuffixTab || ''),
|
|
99
|
+
// R317 / R318 chrome regression.
|
|
100
|
+
r317_inactive_gray_400: probe.layoutInactiveCls.includes('text-gray-400'),
|
|
101
|
+
r318_active_font_medium: probe.layoutActiveCls.includes('font-medium'),
|
|
102
|
+
r294_pulse_absent: probe.pulseCount === 0,
|
|
103
|
+
};
|
|
104
|
+
const ok = Object.values(results).every(Boolean);
|
|
105
|
+
console.log(`${ok ? '✅' : '❌'} prefix opacity-70:`, JSON.stringify(results),
|
|
106
|
+
'\n prefix opacities:', probe.prefixOpacities);
|
|
107
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/* Round 238 verification: prepublishOnly is now a guard-only check
|
|
2
|
+
* instead of a destructive rebuild. This closes the chunk-500 root
|
|
3
|
+
* cause documented since R224.
|
|
4
|
+
*
|
|
5
|
+
* Pre-R238 prepublishOnly was 'rm -rf .next && npm run build', which:
|
|
6
|
+
* 1. Deleted .next/ on disk during npm publish.
|
|
7
|
+
* 2. Rebuilt with fresh content-hashed chunk filenames.
|
|
8
|
+
* 3. Left the already-running next-server process with a stale
|
|
9
|
+
* in-memory manifest pointing at chunks that no longer existed
|
|
10
|
+
* on disk → ENOENT → 500 on lazy chunk fetch.
|
|
11
|
+
* 4. Required a manual 'restart dash AFTER publish' step every
|
|
12
|
+
* round as a workaround.
|
|
13
|
+
*
|
|
14
|
+
* Post-R238 prepublishOnly is a guard:
|
|
15
|
+
* '[ -f .next/BUILD_ID ] || (echo ... && exit 1)'
|
|
16
|
+
*
|
|
17
|
+
* - If .next/BUILD_ID exists, succeed silently — publish proceeds
|
|
18
|
+
* with whatever .next/ is on disk (intentional: pipeline order
|
|
19
|
+
* is now bump → build → restart → test → publish, so the build
|
|
20
|
+
* already embeds the new version BEFORE publish).
|
|
21
|
+
* - If .next/BUILD_ID is missing, fail loudly with the R224
|
|
22
|
+
* reference so the developer knows what to do.
|
|
23
|
+
*
|
|
24
|
+
* No rm-rf-rebuild = running server's manifest stays consistent
|
|
25
|
+
* with disk chunks through the publish step. No post-publish
|
|
26
|
+
* restart needed.
|
|
27
|
+
*
|
|
28
|
+
* Test verifies:
|
|
29
|
+
* 1. package.json scripts.prepublishOnly contains no 'rm' command
|
|
30
|
+
* (destructive form removed)
|
|
31
|
+
* 2. package.json scripts.prepublishOnly contains no 'npm run
|
|
32
|
+
* build' (rebuild on publish removed)
|
|
33
|
+
* 3. The guard form is present (BUILD_ID check)
|
|
34
|
+
* 4. Running `npm run prepublishOnly` exits 0 when .next/BUILD_ID
|
|
35
|
+
* exists, leaving .next/ untouched (mtime + content hash stable)
|
|
36
|
+
*/
|
|
37
|
+
import { readFileSync, statSync } from 'node:fs';
|
|
38
|
+
import { execSync } from 'node:child_process';
|
|
39
|
+
import { createHash } from 'node:crypto';
|
|
40
|
+
|
|
41
|
+
const pkg = JSON.parse(readFileSync('/home/vansin/agent-network-dashboard/package.json', 'utf8'));
|
|
42
|
+
const hookSrc = pkg.scripts?.prepublishOnly || '';
|
|
43
|
+
|
|
44
|
+
// Hash + mtime of .next/BUILD_ID before running the hook
|
|
45
|
+
const buildIdPath = '/home/vansin/agent-network-dashboard/.next/BUILD_ID';
|
|
46
|
+
const before = (() => {
|
|
47
|
+
try {
|
|
48
|
+
return {
|
|
49
|
+
mtime: statSync(buildIdPath).mtimeMs,
|
|
50
|
+
hash: createHash('sha256').update(readFileSync(buildIdPath)).digest('hex'),
|
|
51
|
+
};
|
|
52
|
+
} catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
})();
|
|
56
|
+
|
|
57
|
+
// Run the prepublishOnly hook explicitly via npm run
|
|
58
|
+
let exitCode = -1;
|
|
59
|
+
try {
|
|
60
|
+
execSync('npm run prepublishOnly', {
|
|
61
|
+
cwd: '/home/vansin/agent-network-dashboard',
|
|
62
|
+
stdio: 'pipe',
|
|
63
|
+
});
|
|
64
|
+
exitCode = 0;
|
|
65
|
+
} catch (e) {
|
|
66
|
+
exitCode = e.status ?? -1;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const after = (() => {
|
|
70
|
+
try {
|
|
71
|
+
return {
|
|
72
|
+
mtime: statSync(buildIdPath).mtimeMs,
|
|
73
|
+
hash: createHash('sha256').update(readFileSync(buildIdPath)).digest('hex'),
|
|
74
|
+
};
|
|
75
|
+
} catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
})();
|
|
79
|
+
|
|
80
|
+
const results = {
|
|
81
|
+
hook_present: typeof hookSrc === 'string' && hookSrc.length > 0,
|
|
82
|
+
no_rm_command: !/\brm\b/.test(hookSrc),
|
|
83
|
+
has_build_id_check: /\.next\/BUILD_ID/.test(hookSrc),
|
|
84
|
+
has_exit_path: /exit\s+1/.test(hookSrc),
|
|
85
|
+
hook_exits_zero: exitCode === 0,
|
|
86
|
+
build_id_mtime_stable: before !== null && after !== null && before.mtime === after.mtime,
|
|
87
|
+
build_id_hash_stable: before !== null && after !== null && before.hash === after.hash,
|
|
88
|
+
};
|
|
89
|
+
const ok = Object.values(results).every(Boolean);
|
|
90
|
+
console.log(`${ok ? '✅' : '❌'} prepublishOnly guard:`, JSON.stringify(results),
|
|
91
|
+
'\n hookSrc:', hookSrc,
|
|
92
|
+
'\n exitCode:', exitCode,
|
|
93
|
+
'\n before:', before,
|
|
94
|
+
'\n after: ', after);
|
|
95
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/* Round 210 verification: pressure-bar segments deepen their OWN
|
|
2
|
+
* colour on hover via filter: brightness(1.2). Closes the chip-row
|
|
3
|
+
* "hover deepens own identity" family at a 6th surface (the
|
|
4
|
+
* pressure-bar scope previously had R83 wire setHoveredStatus but
|
|
5
|
+
* the segment itself stayed flat).
|
|
6
|
+
*
|
|
7
|
+
* Pre-R210 hovering a segment fired R55 canvas dim via
|
|
8
|
+
* setHoveredStatus, but the segment's own background stayed at the
|
|
9
|
+
* unmodified tier color. R210 makes the cause element (segment)
|
|
10
|
+
* light up the same gesture it fires on the canvas (effect element).
|
|
11
|
+
*
|
|
12
|
+
* Test (cyber dark, 2 working + 1 idle + 1 offline → 3 segments):
|
|
13
|
+
* 1. 3 [data-pressure-seg] segments rendered.
|
|
14
|
+
* 2. Idle: every segment has hovered='false', filter='none' (or
|
|
15
|
+
* no filter style set).
|
|
16
|
+
* 3. Inline transition includes 'filter 150ms' alongside the
|
|
17
|
+
* existing R165 width 220ms + R60 box-shadow 150ms.
|
|
18
|
+
* 4. Hover working segment → working hovered='true', filter
|
|
19
|
+
* 'brightness(1.2)'.
|
|
20
|
+
* 5. Other (idle/offline) segments stay hovered='false', no filter.
|
|
21
|
+
* 6. Move away → working back to hovered='false', no filter.
|
|
22
|
+
*/
|
|
23
|
+
import { chromium } from 'playwright';
|
|
24
|
+
import { readFileSync } from 'node:fs';
|
|
25
|
+
|
|
26
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
27
|
+
const browser = await chromium.launch({ headless: true });
|
|
28
|
+
|
|
29
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
30
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
31
|
+
await ctx.addInitScript(() => {
|
|
32
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
36
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
37
|
+
const r = await route.fetch();
|
|
38
|
+
const b = await r.json();
|
|
39
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
40
|
+
const mk = (alias, status) => ({
|
|
41
|
+
alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
42
|
+
network_id: nid, project_dir: null,
|
|
43
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
44
|
+
});
|
|
45
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
46
|
+
mk('alpha', 'working'),
|
|
47
|
+
mk('beta', 'working'),
|
|
48
|
+
mk('gamma', 'idle'),
|
|
49
|
+
mk('delta', 'offline'),
|
|
50
|
+
] } });
|
|
51
|
+
});
|
|
52
|
+
await ctx.route('**/api/hub/messages*', (r2) => r2.fulfill({ json: { messages: [] } }));
|
|
53
|
+
await ctx.route('**/api/hub/tasks*', (r2) => r2.fulfill({ json: { tasks: [] } }));
|
|
54
|
+
|
|
55
|
+
const page = await ctx.newPage();
|
|
56
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
57
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
58
|
+
await page.waitForSelector('[data-pressure-seg="working"]', { timeout: 10000 });
|
|
59
|
+
await page.waitForTimeout(300);
|
|
60
|
+
|
|
61
|
+
const probe = async () => page.evaluate(() => {
|
|
62
|
+
return [...document.querySelectorAll('[data-pressure-seg]')].map(el => {
|
|
63
|
+
const cs = getComputedStyle(el);
|
|
64
|
+
return {
|
|
65
|
+
key: el.getAttribute('data-pressure-seg'),
|
|
66
|
+
hovered: el.getAttribute('data-pressure-seg-hovered'),
|
|
67
|
+
filter: cs.filter,
|
|
68
|
+
inlineFilter: el.style.filter,
|
|
69
|
+
transition: el.style.transition,
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const idle = await probe();
|
|
75
|
+
|
|
76
|
+
await page.hover('[data-pressure-seg="working"]');
|
|
77
|
+
await page.waitForTimeout(180);
|
|
78
|
+
const hovered = await probe();
|
|
79
|
+
|
|
80
|
+
await page.mouse.move(0, 0);
|
|
81
|
+
await page.waitForTimeout(180);
|
|
82
|
+
const settled = await probe();
|
|
83
|
+
|
|
84
|
+
await browser.close();
|
|
85
|
+
|
|
86
|
+
const idleWorking = idle.find(s => s.key === 'working');
|
|
87
|
+
const idleIdle = idle.find(s => s.key === 'idle');
|
|
88
|
+
const idleOffline = idle.find(s => s.key === 'offline');
|
|
89
|
+
const hovWorking = hovered.find(s => s.key === 'working');
|
|
90
|
+
const hovIdle = hovered.find(s => s.key === 'idle');
|
|
91
|
+
const settledWorking = settled.find(s => s.key === 'working');
|
|
92
|
+
|
|
93
|
+
const isNoFilter = (s) => !s || s === 'none' || s === '';
|
|
94
|
+
const hasBrightness = (s) => /brightness\s*\(\s*1\.2/.test(s || '');
|
|
95
|
+
|
|
96
|
+
const results = {
|
|
97
|
+
three_segments_present: idle.length === 3,
|
|
98
|
+
working_found: !!idleWorking,
|
|
99
|
+
idle_found: !!idleIdle,
|
|
100
|
+
offline_found: !!idleOffline,
|
|
101
|
+
|
|
102
|
+
// Idle: no filter, hovered='false', transitions present
|
|
103
|
+
idle_all_not_hovered: idle.every(s => s.hovered === 'false'),
|
|
104
|
+
idle_all_no_filter: idle.every(s => isNoFilter(s.inlineFilter)),
|
|
105
|
+
transitions_have_filter: idle.every(s => /filter\s+(?:150ms|0\.15s)/.test(s.transition || '')),
|
|
106
|
+
transitions_keep_width: idle.every(s => /width\s+(?:220ms|0\.22s)/.test(s.transition || '')),
|
|
107
|
+
transitions_keep_bs: idle.every(s => /box-shadow\s+(?:150ms|0\.15s)/.test(s.transition || '')),
|
|
108
|
+
|
|
109
|
+
// Hover working: brightness applied
|
|
110
|
+
hov_working_hovered_true: hovWorking?.hovered === 'true',
|
|
111
|
+
hov_working_has_filter: hasBrightness(hovWorking?.inlineFilter)
|
|
112
|
+
|| hasBrightness(hovWorking?.filter),
|
|
113
|
+
// Other segments stay idle
|
|
114
|
+
hov_idle_stays_false: hovIdle?.hovered === 'false',
|
|
115
|
+
hov_idle_no_filter: isNoFilter(hovIdle?.inlineFilter),
|
|
116
|
+
|
|
117
|
+
// Settled: back to idle
|
|
118
|
+
settled_hovered_false: settledWorking?.hovered === 'false',
|
|
119
|
+
settled_no_filter: isNoFilter(settledWorking?.inlineFilter),
|
|
120
|
+
};
|
|
121
|
+
const ok = Object.values(results).every(Boolean);
|
|
122
|
+
console.log(`${ok ? '✅' : '❌'} pressure-seg self-hover:`, JSON.stringify(results),
|
|
123
|
+
`\n idle:`, idle,
|
|
124
|
+
`\n hovered:`, hovered,
|
|
125
|
+
`\n settled:`, settled);
|
|
126
|
+
process.exit(ok ? 0 : 1);
|