@sleep2agi/agent-network-dashboard 0.4.6 → 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 +107 -0
- package/app/components/HelpOverlay.tsx +109 -1
- package/app/components/ServersDrawer.tsx +181 -5
- package/app/components/TopoGraph.tsx +6409 -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-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-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/0hyl7mdapsz38.js +0 -1
- package/.next/static/chunks/0mqwn-l7wtcdo.js +0 -7
- package/.next/static/chunks/127kw7~wsnyu7.css +0 -2
- package/.next/static/chunks/16yfxp9ak2gdm.js +0 -1
- /package/.next/static/{23MxP0RBVA4BaOJ_HMYmM → 424zX3i80AJ_wHF_otKuP}/_buildManifest.js +0 -0
- /package/.next/static/{23MxP0RBVA4BaOJ_HMYmM → 424zX3i80AJ_wHF_otKuP}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{23MxP0RBVA4BaOJ_HMYmM → 424zX3i80AJ_wHF_otKuP}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/* Round 283 verification: vendor monogram circle strokeWidth bumps
|
|
2
|
+
* 1 → 1.5 per Vincent 5216 "vendor avatar quality" ask increment.
|
|
3
|
+
*
|
|
4
|
+
* Pre-R283: monogram circle (known vendor, no logo asset) rendered
|
|
5
|
+
* with strokeWidth="1". The prefix-group fallback (unknown vendor)
|
|
6
|
+
* also uses strokeWidth="1".
|
|
7
|
+
*
|
|
8
|
+
* Post-R283: monogram (known vendor) bumps to strokeWidth="1.5";
|
|
9
|
+
* prefix-group fallback stays at 1 (less weight = "we don't know
|
|
10
|
+
* what this is" signal preserved).
|
|
11
|
+
*
|
|
12
|
+
* Test trap encountered first draft: each g[data-node] contains
|
|
13
|
+
* THREE small (<r=18) circles —
|
|
14
|
+
* 1. avatar/monogram (r=10-14): strokeWidth 1 or 1.5 per vendor
|
|
15
|
+
* 2. runtime badge (r=5.5-7): strokeWidth ALWAYS 1.5
|
|
16
|
+
* 3. pulse dot (r=2.5): no stroke (null)
|
|
17
|
+
* To isolate the avatar circle, filter by:
|
|
18
|
+
* - r is the largest of the small circles (avatar) OR
|
|
19
|
+
* - exclude elements with [data-runtime-badge] attribute
|
|
20
|
+
* We use the data-attr filter — cleaner intent.
|
|
21
|
+
*/
|
|
22
|
+
import { chromium } from 'playwright';
|
|
23
|
+
import { readFileSync } from 'node:fs';
|
|
24
|
+
|
|
25
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
26
|
+
const browser = await chromium.launch({ headless: true });
|
|
27
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
28
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
29
|
+
await ctx.addInitScript(() => {
|
|
30
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
31
|
+
});
|
|
32
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
33
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
34
|
+
const r = await route.fetch();
|
|
35
|
+
const b = await r.json();
|
|
36
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
37
|
+
const mk = (alias, model) => ({
|
|
38
|
+
alias, status: 'working', model, runtime: 'claude-code-cli',
|
|
39
|
+
network_id: nid, project_dir: null,
|
|
40
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
41
|
+
});
|
|
42
|
+
// Mix: known vendors (Anthropic + OpenAI monogram) + unknown vendor
|
|
43
|
+
// (no model field).
|
|
44
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
45
|
+
mk('alpha', 'claude-opus-4'), // Anthropic, monogram
|
|
46
|
+
mk('beta', 'gpt-4o'), // OpenAI, monogram
|
|
47
|
+
mk('gamma', null), // unknown, prefix-group fallback
|
|
48
|
+
mk('delta', null), // unknown
|
|
49
|
+
] } });
|
|
50
|
+
});
|
|
51
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
52
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
53
|
+
|
|
54
|
+
const page = await ctx.newPage();
|
|
55
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
56
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
57
|
+
await page.waitForSelector('[data-topo-brand-watermark]', { timeout: 10000 });
|
|
58
|
+
await page.waitForTimeout(300);
|
|
59
|
+
|
|
60
|
+
const probe = await page.evaluate(() => {
|
|
61
|
+
// For each node, find the AVATAR circle specifically:
|
|
62
|
+
// - r < 18 (excludes status ring with r ~22-26)
|
|
63
|
+
// - NOT a runtime badge (no data-runtime-badge attr)
|
|
64
|
+
// - has stroke (excludes pulse dot)
|
|
65
|
+
const nodes = [...document.querySelectorAll('g[data-node]')];
|
|
66
|
+
const samples = nodes.map((g) => {
|
|
67
|
+
const alias = g.getAttribute('data-node');
|
|
68
|
+
const circles = [...g.querySelectorAll('circle')];
|
|
69
|
+
const avatar = circles
|
|
70
|
+
.map((c) => ({
|
|
71
|
+
r: +c.getAttribute('r') || 0,
|
|
72
|
+
sw: c.getAttribute('stroke-width'),
|
|
73
|
+
stroke: c.getAttribute('stroke'),
|
|
74
|
+
isBadge: c.hasAttribute('data-runtime-badge'),
|
|
75
|
+
}))
|
|
76
|
+
.filter((c) => c.r > 0 && c.r < 18 && !c.isBadge && c.stroke && c.stroke !== 'none');
|
|
77
|
+
// Of the remaining, the avatar/monogram is the largest-r one.
|
|
78
|
+
avatar.sort((a, b) => b.r - a.r);
|
|
79
|
+
return { alias, avatarStroke: avatar[0]?.sw ?? null, avatarR: avatar[0]?.r ?? null };
|
|
80
|
+
});
|
|
81
|
+
const watermark = document.querySelector('[data-topo-brand-watermark]');
|
|
82
|
+
return { samples, watermarkPresent: watermark !== null };
|
|
83
|
+
});
|
|
84
|
+
await browser.close();
|
|
85
|
+
|
|
86
|
+
const alphaSample = probe.samples.find(s => s.alias === 'alpha');
|
|
87
|
+
const betaSample = probe.samples.find(s => s.alias === 'beta');
|
|
88
|
+
const gammaSample = probe.samples.find(s => s.alias === 'gamma');
|
|
89
|
+
|
|
90
|
+
const results = {
|
|
91
|
+
alpha_anthropic_mono_1_5: alphaSample?.avatarStroke === '1.5',
|
|
92
|
+
beta_openai_mono_1_5: betaSample?.avatarStroke === '1.5',
|
|
93
|
+
gamma_prefix_stays_1: gammaSample?.avatarStroke === '1',
|
|
94
|
+
r282_watermark_present: probe.watermarkPresent,
|
|
95
|
+
};
|
|
96
|
+
const ok = Object.values(results).every(Boolean);
|
|
97
|
+
console.log(`${ok ? '✅' : '❌'} monogram stroke:`, JSON.stringify(results),
|
|
98
|
+
'\n alpha (claude): stroke=', alphaSample?.avatarStroke, ' r=', alphaSample?.avatarR,
|
|
99
|
+
'\n beta (gpt-4o): stroke=', betaSample?.avatarStroke, ' r=', betaSample?.avatarR,
|
|
100
|
+
'\n gamma (unknown): stroke=', gammaSample?.avatarStroke, ' r=', gammaSample?.avatarR);
|
|
101
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/* Round 325 verification: "+N more flows" footer link picks up
|
|
2
|
+
* letterSpacing="0.2" — joins the R285/R289/R301/R302/R304
|
|
3
|
+
* editorial-letterspacing family.
|
|
4
|
+
*
|
|
5
|
+
* The footer is the recent-signal panel's primary navigation
|
|
6
|
+
* affordance into /messages (R128/R133). It carries designed-
|
|
7
|
+
* label semantics ("action label", not row data) but pre-R325
|
|
8
|
+
* sat orphaned from the editorial-spacing axis even though its
|
|
9
|
+
* siblings — kicker/watermark/panel titles/empty hint — all had
|
|
10
|
+
* intentional letter-spacing.
|
|
11
|
+
*
|
|
12
|
+
* 6-axis editorial-letterspacing hierarchy after this round:
|
|
13
|
+
* R285 kicker: 1.2px (eyebrow loud)
|
|
14
|
+
* R289 watermark: 0.5px (wordmark brand)
|
|
15
|
+
* R301 panel titles: 0.3px (section headers)
|
|
16
|
+
* R302 empty main: 0.2px (empty-state hint)
|
|
17
|
+
* R325 footer link: 0.2px (panel nav action) ← this round
|
|
18
|
+
* R304 empty sub: 0.15px (instructional sub)
|
|
19
|
+
*
|
|
20
|
+
* Contract:
|
|
21
|
+
* - [data-recent-panel-more] is present (requires > 3 flowLinks
|
|
22
|
+
* so the footer mounts visible — fixture: 5 message pairs to
|
|
23
|
+
* produce 5 flows).
|
|
24
|
+
* - The element's `letter-spacing` SVG attribute === '0.2' (the
|
|
25
|
+
* literal authored value; computed style normalizes).
|
|
26
|
+
* - R195 cyan-hover regression: [data-recent-panel-more-hovered]
|
|
27
|
+
* defaults to 'false' (no hover simulated).
|
|
28
|
+
* - R259 fontSize regression: fontSize="9" preserved.
|
|
29
|
+
* - R317 / R318 / R294 chrome + pulse regressions intact.
|
|
30
|
+
*/
|
|
31
|
+
import { chromium } from 'playwright';
|
|
32
|
+
import { readFileSync } from 'node:fs';
|
|
33
|
+
|
|
34
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
35
|
+
const browser = await chromium.launch({ headless: true });
|
|
36
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
37
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
38
|
+
await ctx.addInitScript(() => {
|
|
39
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
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) => ({
|
|
47
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
48
|
+
network_id: nid, project_dir: null,
|
|
49
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
50
|
+
});
|
|
51
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
52
|
+
mk('alpha'), mk('beta'), mk('gamma'), mk('delta'), mk('epsilon'),
|
|
53
|
+
] } });
|
|
54
|
+
});
|
|
55
|
+
// 5 distinct flow pairs — recent-signal panel renders top 3 + "+ 2 more flows".
|
|
56
|
+
const now = Date.now();
|
|
57
|
+
const mkMsg = (i, from_alias, to_alias) => ({
|
|
58
|
+
id: `${from_alias}-${to_alias}-${i}`,
|
|
59
|
+
from_alias, to_alias, content: `m${i}`,
|
|
60
|
+
network_id: 'default',
|
|
61
|
+
created_at: new Date(now - (5 + i) * 1000).toISOString(),
|
|
62
|
+
});
|
|
63
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [
|
|
64
|
+
mkMsg(1, 'alpha', 'beta'),
|
|
65
|
+
mkMsg(2, 'beta', 'gamma'),
|
|
66
|
+
mkMsg(3, 'gamma', 'delta'),
|
|
67
|
+
mkMsg(4, 'delta', 'epsilon'),
|
|
68
|
+
mkMsg(5, 'epsilon', 'alpha'),
|
|
69
|
+
] } }));
|
|
70
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
71
|
+
|
|
72
|
+
const page = await ctx.newPage();
|
|
73
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
74
|
+
await page.waitForSelector('[data-recent-panel-more]', { timeout: 15000 });
|
|
75
|
+
await page.waitForTimeout(400);
|
|
76
|
+
|
|
77
|
+
const probe = await page.evaluate(() => {
|
|
78
|
+
const more = document.querySelector('[data-recent-panel-more]');
|
|
79
|
+
return {
|
|
80
|
+
text: more?.textContent ?? null,
|
|
81
|
+
moreCountAttr: more?.getAttribute('data-recent-panel-more') ?? null,
|
|
82
|
+
hoveredAttr: more?.getAttribute('data-recent-panel-more-hovered') ?? null,
|
|
83
|
+
letterSpacing: more?.getAttribute('letter-spacing') ?? null,
|
|
84
|
+
fontSize: more?.getAttribute('font-size') ?? null,
|
|
85
|
+
fontStyle: more?.getAttribute('font-style') ?? null,
|
|
86
|
+
layoutInactiveCls: document.querySelector('[data-topo-chrome-layout="grid"]')?.className ?? '',
|
|
87
|
+
layoutActiveCls: document.querySelector('[data-topo-chrome-layout="ring"]')?.className ?? '',
|
|
88
|
+
pulseCount: document.querySelectorAll('[data-pulse-wrapper]').length,
|
|
89
|
+
};
|
|
90
|
+
});
|
|
91
|
+
await browser.close();
|
|
92
|
+
|
|
93
|
+
const results = {
|
|
94
|
+
more_link_present: probe.text !== null,
|
|
95
|
+
more_text_pattern: /\+\s*\d+\s+more\s+flow/i.test(probe.text || ''),
|
|
96
|
+
more_letter_spacing_0_2: probe.letterSpacing === '0.2',
|
|
97
|
+
// R259 regression — fontSize 9.
|
|
98
|
+
r259_font_size_9: probe.fontSize === '9',
|
|
99
|
+
more_italic: probe.fontStyle === 'italic',
|
|
100
|
+
// R195 regression — default not-hovered state.
|
|
101
|
+
r195_default_not_hovered: probe.hoveredAttr === 'false',
|
|
102
|
+
// R317 / R318 chrome regression.
|
|
103
|
+
r317_inactive_gray_400: probe.layoutInactiveCls.includes('text-gray-400'),
|
|
104
|
+
r318_active_font_medium: probe.layoutActiveCls.includes('font-medium'),
|
|
105
|
+
r294_pulse_absent: probe.pulseCount === 0,
|
|
106
|
+
};
|
|
107
|
+
const ok = Object.values(results).every(Boolean);
|
|
108
|
+
console.log(`${ok ? '✅' : '❌'} more-flows letterSpacing:`, JSON.stringify(results),
|
|
109
|
+
'\n text:', JSON.stringify(probe.text),
|
|
110
|
+
'\n letter-spacing:', probe.letterSpacing);
|
|
111
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/* Round 151 verification: node <g> gains a11y compliance.
|
|
2
|
+
*
|
|
3
|
+
* Node clickable for chat since R45, but tab-unreachable +
|
|
4
|
+
* screen-reader-unannounced. R151 matches the pattern R116 /
|
|
5
|
+
* R139 / R140 / R148 / R149 applied to other interactive
|
|
6
|
+
* surfaces:
|
|
7
|
+
* role="button"
|
|
8
|
+
* tabIndex={0}
|
|
9
|
+
* aria-label="Chat with {alias} ({status})"
|
|
10
|
+
* aria-pressed={chatAlias === session.alias}
|
|
11
|
+
* onKeyDown(Enter / Space) → setChatAlias
|
|
12
|
+
*
|
|
13
|
+
* Three checks:
|
|
14
|
+
* 1. All nodes carry role=button, tabIndex=0, aria-label, aria-
|
|
15
|
+
* pressed=false by default
|
|
16
|
+
* 2. Pressing Enter on a focused node opens chat (aria-pressed
|
|
17
|
+
* flips to true, chat popover renders)
|
|
18
|
+
* 3. Pressing Space on a different node moves the open chat
|
|
19
|
+
* target — only one aria-pressed=true at a time
|
|
20
|
+
*/
|
|
21
|
+
import { chromium } from 'playwright';
|
|
22
|
+
import { readFileSync } from 'node:fs';
|
|
23
|
+
|
|
24
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
25
|
+
const browser = await chromium.launch({ headless: true });
|
|
26
|
+
|
|
27
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
|
|
28
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
29
|
+
await ctx.addInitScript(() => {
|
|
30
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
31
|
+
});
|
|
32
|
+
|
|
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 mk = (alias, status) => ({
|
|
39
|
+
alias, status, model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
40
|
+
network_id: nid, project_dir: null,
|
|
41
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
42
|
+
});
|
|
43
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
44
|
+
mk('alpha', 'working'), mk('beta', 'idle'), mk('gamma', 'working'),
|
|
45
|
+
] } });
|
|
46
|
+
});
|
|
47
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
48
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
49
|
+
|
|
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 === 3, { timeout: 30000 });
|
|
53
|
+
await page.waitForTimeout(400);
|
|
54
|
+
|
|
55
|
+
const inspectAll = () => page.evaluate(() => {
|
|
56
|
+
const nodes = [...document.querySelectorAll('g[data-node]')];
|
|
57
|
+
return nodes.map(n => ({
|
|
58
|
+
alias: n.getAttribute('data-node'),
|
|
59
|
+
role: n.getAttribute('role'),
|
|
60
|
+
tabIndex: n.getAttribute('tabindex'),
|
|
61
|
+
ariaLabel: n.getAttribute('aria-label'),
|
|
62
|
+
ariaPressed: n.getAttribute('aria-pressed'),
|
|
63
|
+
}));
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const before = await inspectAll();
|
|
67
|
+
|
|
68
|
+
// Focus alpha node and press Enter → opens chat
|
|
69
|
+
await page.locator('g[data-node="alpha"]').focus();
|
|
70
|
+
await page.keyboard.press('Enter');
|
|
71
|
+
await page.waitForTimeout(250);
|
|
72
|
+
const afterAlpha = await inspectAll();
|
|
73
|
+
const popoverPresentA = await page.locator('.anet-chat-popover, [data-chat-popover]').count() > 0
|
|
74
|
+
|| await page.evaluate(() => !!document.querySelector('[role="dialog"]'));
|
|
75
|
+
|
|
76
|
+
// Focus gamma node and press Space → moves chat target (only one pressed)
|
|
77
|
+
await page.locator('g[data-node="gamma"]').focus();
|
|
78
|
+
await page.keyboard.press(' ');
|
|
79
|
+
await page.waitForTimeout(250);
|
|
80
|
+
const afterGamma = await inspectAll();
|
|
81
|
+
|
|
82
|
+
await browser.close();
|
|
83
|
+
|
|
84
|
+
const results = {
|
|
85
|
+
// Baseline shape
|
|
86
|
+
threeNodes: before.length === 3,
|
|
87
|
+
allRoleButton: before.every(n => n.role === 'button'),
|
|
88
|
+
allTabIndex0: before.every(n => n.tabIndex === '0'),
|
|
89
|
+
allAriaLabel: before.every(n => /^Chat with /.test(n.ariaLabel || '')),
|
|
90
|
+
allAriaPressedFalse: before.every(n => n.ariaPressed === 'false'),
|
|
91
|
+
|
|
92
|
+
// After Enter on alpha
|
|
93
|
+
enterOpensChat: afterAlpha.find(n => n.alias === 'alpha')?.ariaPressed === 'true',
|
|
94
|
+
popoverShown: popoverPresentA,
|
|
95
|
+
othersStillFalse: afterAlpha.filter(n => n.alias !== 'alpha').every(n => n.ariaPressed === 'false'),
|
|
96
|
+
|
|
97
|
+
// After Space on gamma (chat target moved)
|
|
98
|
+
spaceMovesTarget: afterGamma.find(n => n.alias === 'gamma')?.ariaPressed === 'true',
|
|
99
|
+
alphaUnpressed: afterGamma.find(n => n.alias === 'alpha')?.ariaPressed === 'false',
|
|
100
|
+
onlyOnePressed: afterGamma.filter(n => n.ariaPressed === 'true').length === 1,
|
|
101
|
+
};
|
|
102
|
+
const ok = Object.values(results).every(Boolean);
|
|
103
|
+
console.log(`${ok ? '✅' : '❌'} node a11y:`, JSON.stringify(results),
|
|
104
|
+
`\n before=`, before,
|
|
105
|
+
`\n afterAlpha=`, afterAlpha,
|
|
106
|
+
`\n afterGamma=`, afterGamma);
|
|
107
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/* Round 305 verification: node alias text picks up pin-signature
|
|
2
|
+
* letterSpacing (0.5px) when chatAlias === session.alias. Extends
|
|
3
|
+
* the R219 pin-signature family (recent-row text + legend-row text
|
|
4
|
+
* + edge badge) to the per-node alias label.
|
|
5
|
+
*
|
|
6
|
+
* Test gates: with no chat open, alias should have data-node-alias-
|
|
7
|
+
* chat-target='false'. We cannot easily simulate chat-open without
|
|
8
|
+
* triggering a click + side-effects; this round's primary contract
|
|
9
|
+
* is that the data attribute exists, the transition list includes
|
|
10
|
+
* letter-spacing, and the default (non-chat) state is 0px.
|
|
11
|
+
*
|
|
12
|
+
* Contract:
|
|
13
|
+
* - All [data-node-alias-text] have data-node-alias-chat-target.
|
|
14
|
+
* - Default state attribute is 'false'.
|
|
15
|
+
* - computed transition includes letter-spacing at 0.2s.
|
|
16
|
+
* - All node alias texts have computed letter-spacing 0px in the
|
|
17
|
+
* default state.
|
|
18
|
+
* - R304 sub-hint ls=0.15 + R302 main hint ls=0.2 + R301 panel
|
|
19
|
+
* titles ls=0.3 + R294 pulse absent intact.
|
|
20
|
+
*/
|
|
21
|
+
import { chromium } from 'playwright';
|
|
22
|
+
import { readFileSync } from 'node:fs';
|
|
23
|
+
|
|
24
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
25
|
+
const browser = await chromium.launch({ headless: true });
|
|
26
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
27
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
28
|
+
await ctx.addInitScript(() => {
|
|
29
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
30
|
+
});
|
|
31
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
32
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
33
|
+
const r = await route.fetch();
|
|
34
|
+
const b = await r.json();
|
|
35
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
36
|
+
const mk = (alias, model) => ({
|
|
37
|
+
alias, status: 'working', model, runtime: 'claude-code-cli',
|
|
38
|
+
network_id: nid, project_dir: null,
|
|
39
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
40
|
+
});
|
|
41
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
42
|
+
mk('alpha', 'claude-opus-4'),
|
|
43
|
+
mk('beta', 'gpt-4o'),
|
|
44
|
+
] } });
|
|
45
|
+
});
|
|
46
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
47
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.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.waitForSelector('[data-node-alias-text]', { timeout: 15000 });
|
|
52
|
+
await page.waitForTimeout(400);
|
|
53
|
+
|
|
54
|
+
const probe = await page.evaluate(() => {
|
|
55
|
+
const aliases = [...document.querySelectorAll('[data-node-alias-text]')];
|
|
56
|
+
const samples = aliases.map(a => ({
|
|
57
|
+
alias: a.getAttribute('data-node-alias-text'),
|
|
58
|
+
chatTarget: a.getAttribute('data-node-alias-chat-target'),
|
|
59
|
+
transition: getComputedStyle(a).transition,
|
|
60
|
+
letterSpacing: getComputedStyle(a).letterSpacing,
|
|
61
|
+
}));
|
|
62
|
+
const subhint = document.querySelector('[data-recent-signal-empty-hint]');
|
|
63
|
+
const empty = document.querySelector('[data-recent-signal-empty]');
|
|
64
|
+
const recentTitle = document.querySelector('[data-recent-panel-title]');
|
|
65
|
+
const legendTitle = document.querySelector('[data-legend-panel-title]');
|
|
66
|
+
return {
|
|
67
|
+
samples,
|
|
68
|
+
subhintLs: subhint?.getAttribute('letter-spacing') ?? null,
|
|
69
|
+
emptyLs: empty?.getAttribute('letter-spacing') ?? null,
|
|
70
|
+
recentTitleLs: recentTitle?.getAttribute('letter-spacing') ?? null,
|
|
71
|
+
legendTitleLs: legendTitle?.getAttribute('letter-spacing') ?? null,
|
|
72
|
+
pulseCount: document.querySelectorAll('[data-pulse-wrapper]').length,
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
await browser.close();
|
|
76
|
+
|
|
77
|
+
const hasLsTransition = (s) => /letter-spacing\s+0\.?\d*s|letter-spacing\s+\d+ms/i.test(s || '');
|
|
78
|
+
|
|
79
|
+
const results = {
|
|
80
|
+
alias_texts_present: probe.samples.length >= 2,
|
|
81
|
+
all_have_chat_target_attr: probe.samples.every(s => s.chatTarget === 'true' || s.chatTarget === 'false'),
|
|
82
|
+
all_default_not_chat: probe.samples.every(s => s.chatTarget === 'false'),
|
|
83
|
+
all_transition_has_ls: probe.samples.every(s => hasLsTransition(s.transition)),
|
|
84
|
+
all_default_ls_0: probe.samples.every(s => parseFloat(s.letterSpacing || '0') === 0 || s.letterSpacing === 'normal' || s.letterSpacing === '0px'),
|
|
85
|
+
r304_subhint_ls: probe.subhintLs === '0.15',
|
|
86
|
+
r302_empty_main_ls: probe.emptyLs === '0.2',
|
|
87
|
+
r301_panel_titles_ls: probe.recentTitleLs === '0.3' && probe.legendTitleLs === '0.3',
|
|
88
|
+
r294_pulse_absent: probe.pulseCount === 0,
|
|
89
|
+
};
|
|
90
|
+
const ok = Object.values(results).every(Boolean);
|
|
91
|
+
console.log(`${ok ? '✅' : '❌'} node alias chat-pin signature:`, JSON.stringify(results),
|
|
92
|
+
'\n alias samples:', probe.samples.slice(0, 2),
|
|
93
|
+
'\n R304 sub-hint ls:', probe.subhintLs,
|
|
94
|
+
'\n R302 main hint ls:', probe.emptyLs);
|
|
95
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/* Round 51 verification: hovered node <g> lifts -2px via CSS transform.
|
|
2
|
+
* - At rest, every node's CSS transform is "none" (or matrix with 0 ty).
|
|
3
|
+
* - On hover, the hovered <g> reports `translateY(-2px)` (matrix(1,0,0,1,0,-2))
|
|
4
|
+
* via getComputedStyle.transform, while siblings stay at identity.
|
|
5
|
+
* - Release: returns to identity.
|
|
6
|
+
* - With prefers-reduced-motion forced, NO lift even on hover.
|
|
7
|
+
*/
|
|
8
|
+
import { chromium } from 'playwright';
|
|
9
|
+
import { readFileSync } from 'node:fs';
|
|
10
|
+
|
|
11
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
12
|
+
const browser = await chromium.launch({ headless: true });
|
|
13
|
+
|
|
14
|
+
async function probe(reducedMotion) {
|
|
15
|
+
const ctx = await browser.newContext({
|
|
16
|
+
viewport: { width: 1280, height: 900 },
|
|
17
|
+
reducedMotion: reducedMotion ? 'reduce' : 'no-preference',
|
|
18
|
+
});
|
|
19
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
20
|
+
await ctx.addInitScript(() => {
|
|
21
|
+
try {
|
|
22
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
23
|
+
localStorage.setItem('anet-topo-layout', 'ring');
|
|
24
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
25
|
+
} 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 sessions = ['alpha', 'beta', 'gamma'].map(a => ({
|
|
33
|
+
alias: a, status: 'idle', network_id: nid, project_dir: null,
|
|
34
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
35
|
+
}));
|
|
36
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
37
|
+
});
|
|
38
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
39
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
40
|
+
const page = await ctx.newPage();
|
|
41
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
42
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
|
|
43
|
+
await page.waitForTimeout(500);
|
|
44
|
+
|
|
45
|
+
const readTy = (alias) => page.evaluate(a => {
|
|
46
|
+
const el = document.querySelector(`g[data-node="${a}"]`);
|
|
47
|
+
if (!el) return null;
|
|
48
|
+
const m = getComputedStyle(el).transform;
|
|
49
|
+
if (!m || m === 'none') return 0;
|
|
50
|
+
// matrix(a, b, c, d, tx, ty) — extract ty
|
|
51
|
+
const parts = m.match(/matrix\(([^)]+)\)/);
|
|
52
|
+
if (!parts) return null;
|
|
53
|
+
const arr = parts[1].split(',').map(s => parseFloat(s.trim()));
|
|
54
|
+
return arr[5] ?? null;
|
|
55
|
+
}, alias);
|
|
56
|
+
|
|
57
|
+
const restAlpha = await readTy('alpha');
|
|
58
|
+
const restBeta = await readTy('beta');
|
|
59
|
+
await page.locator('g[data-node="alpha"]').hover({ force: true });
|
|
60
|
+
await page.waitForTimeout(300);
|
|
61
|
+
const hoveredAlpha = await readTy('alpha');
|
|
62
|
+
const hoveredBeta = await readTy('beta');
|
|
63
|
+
await page.mouse.move(10, 10);
|
|
64
|
+
await page.waitForTimeout(300);
|
|
65
|
+
const releasedAlpha = await readTy('alpha');
|
|
66
|
+
|
|
67
|
+
await ctx.close();
|
|
68
|
+
return { restAlpha, restBeta, hoveredAlpha, hoveredBeta, releasedAlpha };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const normal = await probe(false);
|
|
72
|
+
const reduced = await probe(true);
|
|
73
|
+
await browser.close();
|
|
74
|
+
|
|
75
|
+
const results = {
|
|
76
|
+
normal_restAtZero: normal.restAlpha === 0 && normal.restBeta === 0,
|
|
77
|
+
normal_hoveredLifts: normal.hoveredAlpha === -2,
|
|
78
|
+
normal_siblingStays: normal.hoveredBeta === 0,
|
|
79
|
+
normal_released: normal.releasedAlpha === 0,
|
|
80
|
+
reduced_noLift: reduced.hoveredAlpha === 0,
|
|
81
|
+
};
|
|
82
|
+
const ok = Object.values(results).every(Boolean);
|
|
83
|
+
console.log(`${ok ? '✅' : '❌'} node hover lift:`, JSON.stringify(results),
|
|
84
|
+
`\n normal=`, normal, `\n reduced=`, reduced);
|
|
85
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/* Round 194 verification: node label card picks up drop-shadow
|
|
2
|
+
* elevation that intensifies on node hover, mirroring R135's panel
|
|
3
|
+
* hover-elevation but at per-node grain.
|
|
4
|
+
*
|
|
5
|
+
* Pre-R194 the card lifted 1.5 px on hover (R26) but had no shadow
|
|
6
|
+
* follow — the elevation gesture lacked physical weight. R194 adds
|
|
7
|
+
* rest : drop-shadow(0 1px 2px rgba(...,0.30 dark / 0.08 light))
|
|
8
|
+
* hover : drop-shadow(0 4px 12px rgba(0,0,0,0.60) dark
|
|
9
|
+
* / 0 3px 8px rgba(15,23,42,0.20) light)
|
|
10
|
+
* with transition: filter 220ms ease-out.
|
|
11
|
+
*
|
|
12
|
+
* Test (cyber/dark theme, 4 sessions = card mode):
|
|
13
|
+
* 1. data-node-label-card present + matches alias
|
|
14
|
+
* 2. Idle elevation attr = 'idle'
|
|
15
|
+
* 3. Idle filter style includes 'drop-shadow' (rest shadow on)
|
|
16
|
+
* 4. Idle blur radius is small (1-2 px range)
|
|
17
|
+
* 5. transition: filter 220ms ease-out
|
|
18
|
+
* 6. Hover the node — wait past 250ms
|
|
19
|
+
* 7. data-node-label-card-elevation flips to 'hover'
|
|
20
|
+
* 8. Hover filter has bigger blur (>= 8px) — drop-shadow strengthened
|
|
21
|
+
* 9. Other (non-hovered) cards stay 'idle'
|
|
22
|
+
* 10. Unhover — flips back to 'idle'
|
|
23
|
+
* 11. Filter transition string still present after settle
|
|
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
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
52
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
53
|
+
|
|
54
|
+
const page = await ctx.newPage();
|
|
55
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
56
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
57
|
+
await page.waitForSelector('[data-node-label-card]', { timeout: 10000 });
|
|
58
|
+
await page.waitForTimeout(400);
|
|
59
|
+
|
|
60
|
+
// Probe idle state of alpha's label card
|
|
61
|
+
const idleAlpha = await page.evaluate(() => {
|
|
62
|
+
const el = document.querySelector('[data-node-label-card="alpha"]');
|
|
63
|
+
if (!el) return null;
|
|
64
|
+
const cs = getComputedStyle(el);
|
|
65
|
+
return {
|
|
66
|
+
alias: el.getAttribute('data-node-label-card'),
|
|
67
|
+
elevation: el.getAttribute('data-node-label-card-elevation'),
|
|
68
|
+
filter: cs.filter,
|
|
69
|
+
transition: cs.transitionProperty + ' / ' + cs.transitionDuration,
|
|
70
|
+
inlineFilter: el.style.filter,
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Hover the alpha node's <g> wrapper
|
|
75
|
+
await page.hover('g[data-node="alpha"]');
|
|
76
|
+
await page.waitForTimeout(280); // past 220ms transition
|
|
77
|
+
|
|
78
|
+
const hoveredAlpha = await page.evaluate(() => {
|
|
79
|
+
const el = document.querySelector('[data-node-label-card="alpha"]');
|
|
80
|
+
const cs = getComputedStyle(el);
|
|
81
|
+
return {
|
|
82
|
+
elevation: el.getAttribute('data-node-label-card-elevation'),
|
|
83
|
+
filter: cs.filter,
|
|
84
|
+
inlineFilter: el.style.filter,
|
|
85
|
+
};
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Probe non-hovered card (beta) stays idle
|
|
89
|
+
const idleBeta = await page.evaluate(() => {
|
|
90
|
+
const el = document.querySelector('[data-node-label-card="beta"]');
|
|
91
|
+
return {
|
|
92
|
+
elevation: el.getAttribute('data-node-label-card-elevation'),
|
|
93
|
+
};
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Move away, wait for transition back
|
|
97
|
+
await page.mouse.move(0, 0);
|
|
98
|
+
await page.waitForTimeout(280);
|
|
99
|
+
|
|
100
|
+
const settledAlpha = await page.evaluate(() => {
|
|
101
|
+
const el = document.querySelector('[data-node-label-card="alpha"]');
|
|
102
|
+
const cs = getComputedStyle(el);
|
|
103
|
+
return {
|
|
104
|
+
elevation: el.getAttribute('data-node-label-card-elevation'),
|
|
105
|
+
filter: cs.filter,
|
|
106
|
+
transition: cs.transitionProperty + ' / ' + cs.transitionDuration,
|
|
107
|
+
};
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
await browser.close();
|
|
111
|
+
|
|
112
|
+
// Parse blur radius out of drop-shadow(). Chrome's getComputedStyle
|
|
113
|
+
// canonicalises into `drop-shadow(rgba(...) 0px Ypx BLURpx)` with the
|
|
114
|
+
// color first — the standard write-order `drop-shadow(0 Ypx BLURpx
|
|
115
|
+
// rgba(...))` is rearranged. So scan all numeric tokens inside the
|
|
116
|
+
// drop-shadow() parens with rgba/hsla content stripped first; blur is
|
|
117
|
+
// the 3rd numeric value.
|
|
118
|
+
function parseShadowBlur(filterStr) {
|
|
119
|
+
if (!filterStr) return null;
|
|
120
|
+
const m = filterStr.match(/drop-shadow\(([\s\S]+)\)\s*$/);
|
|
121
|
+
if (!m) return null;
|
|
122
|
+
const stripped = m[1].replace(/(rgba?|hsla?)\([^)]*\)/g, '');
|
|
123
|
+
const nums = (stripped.match(/-?\d+(?:\.\d+)?/g) || []).map(Number);
|
|
124
|
+
return nums.length >= 3 ? Math.abs(nums[2]) : null;
|
|
125
|
+
}
|
|
126
|
+
const idleBlur = parseShadowBlur(idleAlpha?.filter);
|
|
127
|
+
const hoverBlur = parseShadowBlur(hoveredAlpha?.filter);
|
|
128
|
+
const settledBlur = parseShadowBlur(settledAlpha?.filter);
|
|
129
|
+
|
|
130
|
+
const results = {
|
|
131
|
+
card_present: !!idleAlpha,
|
|
132
|
+
alias_matches: idleAlpha?.alias === 'alpha',
|
|
133
|
+
idle_elevation_attr: idleAlpha?.elevation === 'idle',
|
|
134
|
+
idle_has_dropshadow: (idleAlpha?.filter || '').includes('drop-shadow'),
|
|
135
|
+
idle_blur_small: idleBlur !== null && idleBlur > 0 && idleBlur <= 3,
|
|
136
|
+
filter_transition: idleAlpha?.transition.includes('filter') && idleAlpha?.transition.includes('0.22s'),
|
|
137
|
+
|
|
138
|
+
hover_elevation_attr: hoveredAlpha?.elevation === 'hover',
|
|
139
|
+
hover_has_dropshadow: (hoveredAlpha?.filter || '').includes('drop-shadow'),
|
|
140
|
+
hover_blur_larger: hoverBlur !== null && hoverBlur >= 8,
|
|
141
|
+
hover_blur_grew: idleBlur !== null && hoverBlur !== null && hoverBlur > idleBlur,
|
|
142
|
+
hover_filter_changed: idleAlpha?.filter !== hoveredAlpha?.filter,
|
|
143
|
+
|
|
144
|
+
beta_stays_idle: idleBeta?.elevation === 'idle',
|
|
145
|
+
|
|
146
|
+
settled_back_to_idle: settledAlpha?.elevation === 'idle',
|
|
147
|
+
settled_blur_small: settledBlur !== null && settledBlur <= 3,
|
|
148
|
+
settled_keeps_transition: settledAlpha?.transition.includes('filter'),
|
|
149
|
+
};
|
|
150
|
+
const ok = Object.values(results).every(Boolean);
|
|
151
|
+
console.log(`${ok ? '✅' : '❌'} node label card elevation:`, JSON.stringify(results),
|
|
152
|
+
`\n idleAlpha:`, idleAlpha,
|
|
153
|
+
`\n hoveredAlpha:`, hoveredAlpha,
|
|
154
|
+
`\n idleBeta:`, idleBeta,
|
|
155
|
+
`\n settledAlpha:`, settledAlpha,
|
|
156
|
+
`\n blurs:`, { idleBlur, hoverBlur, settledBlur });
|
|
157
|
+
process.exit(ok ? 0 : 1);
|