@sleep2agi/agent-network-dashboard 0.4.6-preview.27 → 0.4.6-preview.270
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/BUILD_ID +1 -1
- package/.next/app-path-routes-manifest.json +1 -0
- package/.next/build-manifest.json +3 -3
- package/.next/diagnostics/route-bundle-stats.json +41 -41
- package/.next/fallback-build-manifest.json +3 -3
- package/.next/prerender-manifest.json +3 -3
- package/.next/routes-manifest.json +6 -0
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +2 -2
- package/.next/server/app/_not-found.rsc +12 -12
- package/.next/server/app/_not-found.segments/_full.segment.rsc +12 -12
- package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/server/app/_not-found.segments/_index.segment.rsc +7 -7
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
- package/.next/server/app/admin.html +2 -2
- package/.next/server/app/admin.rsc +14 -14
- package/.next/server/app/admin.segments/_full.segment.rsc +14 -14
- package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
- package/.next/server/app/admin.segments/_index.segment.rsc +7 -7
- package/.next/server/app/admin.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/admin.segments/admin.segment.rsc +3 -3
- package/.next/server/app/api/hub/servers/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/hub/servers/route/build-manifest.json +9 -0
- package/.next/server/app/api/hub/servers/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/hub/servers/route.js +7 -0
- package/.next/server/app/api/hub/servers/route.js.map +5 -0
- package/.next/server/app/api/hub/servers/route.js.nft.json +1 -0
- package/.next/server/app/api/hub/servers/route_client-reference-manifest.js +3 -0
- package/.next/server/app/index.html +2 -2
- package/.next/server/app/index.rsc +14 -14
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/index.segments/_full.segment.rsc +14 -14
- package/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/server/app/index.segments/_index.segment.rsc +7 -7
- package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/login.html +2 -2
- package/.next/server/app/login.rsc +14 -14
- package/.next/server/app/login.segments/_full.segment.rsc +14 -14
- package/.next/server/app/login.segments/_head.segment.rsc +4 -4
- package/.next/server/app/login.segments/_index.segment.rsc +7 -7
- package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/login.segments/login.segment.rsc +3 -3
- package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/logs.html +2 -2
- package/.next/server/app/logs.rsc +14 -14
- package/.next/server/app/logs.segments/_full.segment.rsc +14 -14
- package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
- package/.next/server/app/logs.segments/_index.segment.rsc +7 -7
- package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
- package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
- package/.next/server/app/messages.html +2 -2
- package/.next/server/app/messages.rsc +14 -14
- package/.next/server/app/messages.segments/_full.segment.rsc +14 -14
- package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
- package/.next/server/app/messages.segments/_index.segment.rsc +7 -7
- package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
- package/.next/server/app/node/page_client-reference-manifest.js +1 -1
- package/.next/server/app/node.html +2 -2
- package/.next/server/app/node.rsc +14 -14
- package/.next/server/app/node.segments/_full.segment.rsc +14 -14
- package/.next/server/app/node.segments/_head.segment.rsc +4 -4
- package/.next/server/app/node.segments/_index.segment.rsc +7 -7
- package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/node.segments/node.segment.rsc +3 -3
- package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
- package/.next/server/app/nodes.html +2 -2
- package/.next/server/app/nodes.rsc +14 -14
- package/.next/server/app/nodes.segments/_full.segment.rsc +14 -14
- package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
- package/.next/server/app/nodes.segments/_index.segment.rsc +7 -7
- package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/server-logs.html +2 -2
- package/.next/server/app/server-logs.rsc +14 -14
- package/.next/server/app/server-logs.segments/_full.segment.rsc +14 -14
- package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
- package/.next/server/app/server-logs.segments/_index.segment.rsc +7 -7
- package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
- package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/networks.html +2 -2
- package/.next/server/app/settings/networks.rsc +14 -14
- package/.next/server/app/settings/networks.segments/_full.segment.rsc +14 -14
- package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings/networks.segments/_index.segment.rsc +7 -7
- package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
- package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
- package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/tokens.html +2 -2
- package/.next/server/app/settings/tokens.rsc +14 -14
- package/.next/server/app/settings/tokens.segments/_full.segment.rsc +14 -14
- package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings/tokens.segments/_index.segment.rsc +7 -7
- package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
- package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
- package/.next/server/app/settings.html +2 -2
- package/.next/server/app/settings.rsc +14 -14
- package/.next/server/app/settings.segments/_full.segment.rsc +14 -14
- package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings.segments/_index.segment.rsc +7 -7
- package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
- package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
- package/.next/server/app/tasks.html +2 -2
- package/.next/server/app/tasks.rsc +14 -14
- package/.next/server/app/tasks.segments/_full.segment.rsc +14 -14
- package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
- package/.next/server/app/tasks.segments/_index.segment.rsc +7 -7
- package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
- package/.next/server/app-paths-manifest.json +1 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_servers_route_actions_08-m-cs.js +3 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_servers_route_actions_08-m-cs.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0tx8s8i._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0tx8s8i._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +4 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_057q.ne._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_057q.ne._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js.map +1 -1
- package/.next/server/middleware-build-manifest.js +3 -3
- package/.next/server/pages/404.html +2 -2
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.js +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/0-mpa_947ipeq.js +1 -0
- package/.next/static/chunks/0.qg_sd3s8sj~.js +1 -0
- package/.next/static/chunks/01lmbsd37fybu.js +1 -0
- package/.next/static/chunks/03o.h6kvmw4l_.js +1 -0
- package/.next/static/chunks/03~~oirxz7~vc.js +1 -0
- package/.next/static/chunks/05uk96gc~9mni.js +1 -0
- package/.next/static/chunks/08kf-.bgs10ze.js +1 -0
- package/.next/static/chunks/0ggj5ummw2a84.js +7 -0
- package/.next/static/chunks/0glmrdj9112tc.js +7 -0
- package/.next/static/chunks/0rxrez_cjn.eh.js +1 -0
- package/.next/static/chunks/0x8x.29hkju5a.js +1 -0
- package/.next/static/chunks/0y.7kwty.hl7h.css +2 -0
- package/.next/static/chunks/0zwpi6hqmzpqh.js +1 -0
- package/.next/static/chunks/149kodgion8f0.js +4 -0
- package/.next/static/chunks/174re5ar~kxne.js +1 -0
- package/.next/trace +2 -2
- package/.next/trace-build +1 -1
- package/.next/types/routes.d.ts +2 -1
- package/.next/types/validator.ts +9 -0
- package/app/api/hub/servers/route.ts +39 -0
- package/app/components/ChatPopover.tsx +38 -2
- package/app/components/CommandPalette.tsx +107 -0
- package/app/components/HelpOverlay.tsx +126 -0
- package/app/components/ServersDrawer.tsx +64 -27
- package/app/components/TopoGraph.tsx +5411 -240
- package/app/components/utils.ts +8 -7
- package/app/globals.css +110 -5
- package/app/lib/time.ts +36 -0
- package/package.json +2 -2
- package/scripts/help-overlay-topo-test.mjs +44 -0
- package/scripts/servers-drawer-live-test.mjs +96 -0
- package/scripts/topo-active-filter-pill-test.mjs +101 -0
- package/scripts/topo-active-links-chip-hover-test.mjs +103 -0
- package/scripts/topo-active-links-chip-nav-test.mjs +117 -0
- package/scripts/topo-active-links-chip-self-hover-test.mjs +171 -0
- package/scripts/topo-active-links-chip-test.mjs +73 -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-arrival-ping-test.mjs +87 -0
- package/scripts/topo-auto-fit-test.mjs +73 -0
- package/scripts/topo-canvas-aria-test.mjs +108 -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-semantic-gap-test.mjs +97 -0
- package/scripts/topo-chip-row-tabular-test.mjs +111 -0
- package/scripts/topo-chrome-a11y-test.mjs +107 -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-zoom-level-pop-test.mjs +122 -0
- package/scripts/topo-chrome-zoom-pop-test.mjs +130 -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-cwd-tooltip-test.mjs +64 -0
- package/scripts/topo-dblclick-reset-test.mjs +74 -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-dispatch-pulse-test.mjs +83 -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 +93 -0
- package/scripts/topo-edge-pill-hot-test.mjs +129 -0
- package/scripts/topo-edge-pill-test.mjs +105 -0
- package/scripts/topo-edge-smil-sync-test.mjs +148 -0
- package/scripts/topo-edge-smil-theme-ease-test.mjs +97 -0
- package/scripts/topo-edge-stroke-ease-test.mjs +100 -0
- package/scripts/topo-edge-stroke-transition-test.mjs +132 -0
- package/scripts/topo-edge-tooltip-test.mjs +70 -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-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-test.mjs +110 -0
- package/scripts/topo-filter-pill-mobile-test.mjs +90 -0
- package/scripts/topo-fit-key-test.mjs +106 -0
- package/scripts/topo-freshness-chip-test.mjs +74 -0
- package/scripts/topo-freshness-chip-transition-test.mjs +80 -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 +100 -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-halo-breath-ease-test.mjs +92 -0
- package/scripts/topo-halo-breath-stagger-test.mjs +89 -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 +93 -0
- package/scripts/topo-hub-working-count-test.mjs +99 -0
- package/scripts/topo-ingroup-edge-test.mjs +103 -0
- package/scripts/topo-label-card-theme-ease-test.mjs +85 -0
- package/scripts/topo-label-lift-test.mjs +119 -0
- package/scripts/topo-lastseen-tooltip-test.mjs +59 -0
- package/scripts/topo-layout-key-test.mjs +91 -0
- package/scripts/topo-layout-switch-crossfade-test.mjs +126 -0
- package/scripts/topo-layout-toggle-test.mjs +131 -0
- package/scripts/topo-legend-count-empty-test.mjs +126 -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-header-symmetric-inset-test.mjs +112 -0
- package/scripts/topo-legend-hover-test.mjs +113 -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-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-minimap-a11y-test.mjs +101 -0
- package/scripts/topo-minimap-dot-transition-test.mjs +113 -0
- package/scripts/topo-minimap-test.mjs +68 -0
- package/scripts/topo-minimap-viewport-smooth-test.mjs +124 -0
- package/scripts/topo-mobile-chips-test.mjs +103 -0
- package/scripts/topo-node-a11y-test.mjs +107 -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-busyness-test.mjs +96 -0
- package/scripts/topo-orbit-opacity-bucket-test.mjs +124 -0
- package/scripts/topo-overlap-test.mjs +32 -5
- package/scripts/topo-pan-cursor-test.mjs +62 -0
- package/scripts/topo-panel-elevation-test.mjs +76 -0
- package/scripts/topo-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-theme-ease-test.mjs +85 -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-ping-pulse-ease-test.mjs +96 -0
- package/scripts/topo-popover-meta-test.mjs +78 -0
- package/scripts/topo-prepublish-guard-test.mjs +95 -0
- package/scripts/topo-pressure-bar-test.mjs +69 -0
- package/scripts/topo-pressure-hover-test.mjs +123 -0
- package/scripts/topo-pressure-pin-test.mjs +119 -0
- package/scripts/topo-pressure-seg-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-rate-test.mjs +71 -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-footer-breath-test.mjs +142 -0
- package/scripts/topo-recent-panel-hot-header-test.mjs +132 -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-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-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-recent-signal-empty-test.mjs +63 -0
- package/scripts/topo-reduced-motion-test.mjs +77 -0
- package/scripts/topo-reset-spin-test.mjs +120 -0
- package/scripts/topo-runtime-badge-grow-test.mjs +114 -0
- package/scripts/topo-spoke-busyness-test.mjs +110 -0
- package/scripts/topo-spoke-tint-test.mjs +96 -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-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-tz-ghost-test.mjs +69 -0
- package/scripts/topo-vendor-chip-hover-test.mjs +111 -0
- package/scripts/topo-vendor-chip-self-hover-test.mjs +130 -0
- package/scripts/topo-vendor-chip-tabular-test.mjs +89 -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-wheel-zoom-test.mjs +79 -0
- package/scripts/topo-working-chip-pin-test.mjs +154 -0
- package/scripts/topo-working-halo-breath-test.mjs +86 -0
- package/scripts/topo-wrapper-flow-arrow-theme-test.mjs +97 -0
- package/scripts/topo-wrapper-shadow-theme-ease-test.mjs +89 -0
- package/scripts/topo-zoom-keys-test.mjs +89 -0
- package/.next/static/chunks/0-7iowzu4.798.js +0 -1
- package/.next/static/chunks/02t5jr4shh.hy.js +0 -1
- package/.next/static/chunks/04ppgi-mphhr6.js +0 -1
- package/.next/static/chunks/09nd.t_hd.y6t.css +0 -2
- package/.next/static/chunks/0_qfg5s5uzs7..js +0 -7
- package/.next/static/chunks/0elalb9n_.~di.js +0 -1
- package/.next/static/chunks/0fqy_0wj_1xa7.js +0 -7
- package/.next/static/chunks/0j_q__o8p6g83.js +0 -1
- package/.next/static/chunks/0jl6_w50zh4l5.js +0 -1
- package/.next/static/chunks/0r4q73.-1bkyl.js +0 -1
- package/.next/static/chunks/0r_c8eccg3z2o.js +0 -1
- package/.next/static/chunks/0zs511wcsq~0j.js +0 -1
- package/.next/static/chunks/10oi7l-tycgl9.js +0 -1
- package/.next/static/chunks/11uk1izuv3~0t.js +0 -1
- package/.next/static/chunks/15gj.t675_yw3.js +0 -1
- /package/.next/static/{kk1qr2sXSqVvoqBT7fk1x → zfTklPhuNFS7pf_LmLAvq}/_buildManifest.js +0 -0
- /package/.next/static/{kk1qr2sXSqVvoqBT7fk1x → zfTklPhuNFS7pf_LmLAvq}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{kk1qr2sXSqVvoqBT7fk1x → zfTklPhuNFS7pf_LmLAvq}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/* Round 168 verification: smooth viewport transition on reset/fit.
|
|
2
|
+
*
|
|
3
|
+
* Pre-R168 resetView/fitView snapped the <g transform> from
|
|
4
|
+
* current zoom/pan to {zoom:1, x:0, y:0} in one frame. Pressing
|
|
5
|
+
* `0` or clicking the hub jolted the eye.
|
|
6
|
+
*
|
|
7
|
+
* R168 arms a one-shot smoothView flag when resetView/fitView
|
|
8
|
+
* fires; the viewport <g> reads this flag and conditionally
|
|
9
|
+
* applies `transition: transform 300ms ease-out`. Auto-clears
|
|
10
|
+
* 350ms later. Pan (R103 drag) and wheel zoom never set the
|
|
11
|
+
* flag, so interactive view changes stay snappy.
|
|
12
|
+
*
|
|
13
|
+
* Test:
|
|
14
|
+
* 1. Load page → idle state, smoothView=false (no transition)
|
|
15
|
+
* 2. Zoom in twice via '+' to set non-default view
|
|
16
|
+
* 3. Press '0' (resetView) → smoothView=true, viewport <g>
|
|
17
|
+
* has style.transition = 'transform 300ms ease-out'
|
|
18
|
+
* 4. Wait 450ms → smoothView=false again, transition cleared
|
|
19
|
+
* 5. Repeat with 'f' (fitView)
|
|
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: 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
|
+
|
|
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) => ({
|
|
39
|
+
alias, status: 'working', 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: [mk('alpha'), mk('beta'), mk('gamma')] } });
|
|
44
|
+
});
|
|
45
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
46
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
47
|
+
|
|
48
|
+
const page = await ctx.newPage();
|
|
49
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
50
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
|
|
51
|
+
await page.waitForSelector('[data-topo-viewport]', { timeout: 10000 });
|
|
52
|
+
await page.waitForTimeout(400);
|
|
53
|
+
|
|
54
|
+
// Idle: smoothView=false, no transition style.
|
|
55
|
+
const idle = await page.evaluate(() => {
|
|
56
|
+
const g = document.querySelector('[data-topo-viewport]');
|
|
57
|
+
return {
|
|
58
|
+
smooth: g?.getAttribute('data-topo-viewport-smooth'),
|
|
59
|
+
transition: g?.style?.transition || '',
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Zoom in twice → set non-default view
|
|
64
|
+
await page.keyboard.press('+');
|
|
65
|
+
await page.keyboard.press('+');
|
|
66
|
+
await page.waitForTimeout(150);
|
|
67
|
+
const zoomBefore = await page.evaluate(() =>
|
|
68
|
+
document.querySelector('[data-topo-chrome-zoom-level]')?.textContent?.trim());
|
|
69
|
+
|
|
70
|
+
// Press 0 (resetView) → smoothView=true. Probe within the
|
|
71
|
+
// 350ms window so the style is still set.
|
|
72
|
+
await page.keyboard.press('0');
|
|
73
|
+
// Small delay so React commits the state update.
|
|
74
|
+
await page.waitForTimeout(50);
|
|
75
|
+
const duringReset = await page.evaluate(() => {
|
|
76
|
+
const g = document.querySelector('[data-topo-viewport]');
|
|
77
|
+
return {
|
|
78
|
+
smooth: g?.getAttribute('data-topo-viewport-smooth'),
|
|
79
|
+
transition: g?.style?.transition || '',
|
|
80
|
+
};
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Wait past the 350ms window
|
|
84
|
+
await page.waitForTimeout(450);
|
|
85
|
+
const afterReset = await page.evaluate(() => {
|
|
86
|
+
const g = document.querySelector('[data-topo-viewport]');
|
|
87
|
+
return {
|
|
88
|
+
smooth: g?.getAttribute('data-topo-viewport-smooth'),
|
|
89
|
+
transition: g?.style?.transition || '',
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
const zoomAfter = await page.evaluate(() =>
|
|
93
|
+
document.querySelector('[data-topo-chrome-zoom-level]')?.textContent?.trim());
|
|
94
|
+
|
|
95
|
+
// Now zoom in again and press 'f' (fitView) — same arming path
|
|
96
|
+
await page.keyboard.press('+');
|
|
97
|
+
await page.keyboard.press('+');
|
|
98
|
+
await page.waitForTimeout(150);
|
|
99
|
+
await page.keyboard.press('f');
|
|
100
|
+
await page.waitForTimeout(50);
|
|
101
|
+
const duringFit = await page.evaluate(() => {
|
|
102
|
+
const g = document.querySelector('[data-topo-viewport]');
|
|
103
|
+
return {
|
|
104
|
+
smooth: g?.getAttribute('data-topo-viewport-smooth'),
|
|
105
|
+
transition: g?.style?.transition || '',
|
|
106
|
+
};
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
await browser.close();
|
|
110
|
+
|
|
111
|
+
const results = {
|
|
112
|
+
viewport_found: idle !== null,
|
|
113
|
+
idle_smooth_false: idle.smooth === 'false',
|
|
114
|
+
idle_no_transition: !idle.transition.includes('transform'),
|
|
115
|
+
zoomed_before_reset: zoomBefore !== '100%',
|
|
116
|
+
reset_armed_smooth: duringReset.smooth === 'true',
|
|
117
|
+
reset_transition_set: duringReset.transition.includes('transform 300ms'),
|
|
118
|
+
after_reset_smooth_false: afterReset.smooth === 'false',
|
|
119
|
+
after_reset_no_transition: !afterReset.transition.includes('transform'),
|
|
120
|
+
reset_landed_at_100: zoomAfter === '100%',
|
|
121
|
+
fit_armed_smooth: duringFit.smooth === 'true',
|
|
122
|
+
fit_transition_set: duringFit.transition.includes('transform 300ms'),
|
|
123
|
+
};
|
|
124
|
+
const ok = Object.values(results).every(Boolean);
|
|
125
|
+
console.log(`${ok ? '✅' : '❌'} view smooth transition:`, JSON.stringify(results),
|
|
126
|
+
`\n idle =`, idle,
|
|
127
|
+
`\n duringReset =`, duringReset,
|
|
128
|
+
`\n afterReset =`, afterReset,
|
|
129
|
+
`\n duringFit =`, duringFit,
|
|
130
|
+
`\n zoom before/after = ${zoomBefore} → ${zoomAfter}`);
|
|
131
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/* Round 23 verification: wheel-zoom is guarded inline (requires
|
|
2
|
+
* ctrl/meta) but unconditional in fullscreen. Plain wheel over the
|
|
3
|
+
* canvas must let page scroll through. */
|
|
4
|
+
import { chromium } from 'playwright';
|
|
5
|
+
import { readFileSync } from 'node:fs';
|
|
6
|
+
|
|
7
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
8
|
+
const browser = await chromium.launch({ headless: true });
|
|
9
|
+
const ctx = await browser.newContext({ viewport: { width: 1280, height: 1600 } });
|
|
10
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
11
|
+
await ctx.addInitScript(() => {
|
|
12
|
+
try {
|
|
13
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
14
|
+
localStorage.setItem('anet-topo-layout', 'grid');
|
|
15
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
16
|
+
} catch {}
|
|
17
|
+
});
|
|
18
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
19
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
20
|
+
const r = await route.fetch();
|
|
21
|
+
const b = await r.json();
|
|
22
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
23
|
+
const sessions = ['a', 'b'].map(a => ({
|
|
24
|
+
alias: a, status: 'idle', network_id: nid, project_dir: null,
|
|
25
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
26
|
+
}));
|
|
27
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
28
|
+
});
|
|
29
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
30
|
+
const page = await ctx.newPage();
|
|
31
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
32
|
+
await page.waitForSelector('svg[viewBox="0 0 1000 680"]', { timeout: 30000 });
|
|
33
|
+
await page.waitForTimeout(400);
|
|
34
|
+
|
|
35
|
+
const readZoom = () => page.evaluate(() => {
|
|
36
|
+
const span = document.querySelector('button[aria-label="Zoom in"]').parentElement.querySelector('span[title*="zoom level"]');
|
|
37
|
+
return +span.textContent.replace('%', '');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Move cursor over the SVG centre
|
|
41
|
+
const box = await page.$eval('svg[viewBox="0 0 1000 680"]', el => el.getBoundingClientRect());
|
|
42
|
+
const cx = box.x + box.width / 2;
|
|
43
|
+
const cy = box.y + box.height / 2;
|
|
44
|
+
await page.mouse.move(cx, cy);
|
|
45
|
+
const start = await readZoom();
|
|
46
|
+
|
|
47
|
+
// PLAIN wheel — must NOT zoom (guard active inline)
|
|
48
|
+
await page.mouse.wheel(0, -100);
|
|
49
|
+
await page.waitForTimeout(80);
|
|
50
|
+
const afterPlainWheel = await readZoom();
|
|
51
|
+
|
|
52
|
+
// PLAIN wheel must let page scroll
|
|
53
|
+
const scrollBefore = await page.evaluate(() => window.scrollY);
|
|
54
|
+
await page.mouse.wheel(0, 200);
|
|
55
|
+
await page.waitForTimeout(80);
|
|
56
|
+
const scrollAfter = await page.evaluate(() => window.scrollY);
|
|
57
|
+
// scroll back so the next test has the SVG in view
|
|
58
|
+
await page.evaluate(() => window.scrollTo(0, 0));
|
|
59
|
+
await page.mouse.move(cx, cy);
|
|
60
|
+
await page.waitForTimeout(100);
|
|
61
|
+
|
|
62
|
+
// CTRL+wheel — must zoom inline
|
|
63
|
+
const beforeCtrl = await readZoom();
|
|
64
|
+
await page.keyboard.down('Control');
|
|
65
|
+
await page.mouse.wheel(0, -100);
|
|
66
|
+
await page.keyboard.up('Control');
|
|
67
|
+
await page.waitForTimeout(80);
|
|
68
|
+
const afterCtrl = await readZoom();
|
|
69
|
+
|
|
70
|
+
await browser.close();
|
|
71
|
+
const results = {
|
|
72
|
+
plainWheelDoesNotZoom: afterPlainWheel === start,
|
|
73
|
+
plainWheelPageScrolls: scrollAfter > scrollBefore,
|
|
74
|
+
ctrlWheelZooms: afterCtrl > beforeCtrl,
|
|
75
|
+
};
|
|
76
|
+
const ok = Object.values(results).every(Boolean);
|
|
77
|
+
console.log(`${ok ? '✅' : '❌'} wheel zoom guard:`, JSON.stringify(results),
|
|
78
|
+
`start=${start} plainWheel=${afterPlainWheel} scrollBefore=${scrollBefore} scrollAfter=${scrollAfter} beforeCtrl=${beforeCtrl} afterCtrl=${afterCtrl}`);
|
|
79
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/* Round 139 verification: the chip-row "N working" chip click
|
|
2
|
+
* pins pinnedStatus='working'. Closes the cursor-pointer-lying
|
|
3
|
+
* bug from R79 — the chip's tooltip has been promising "click to
|
|
4
|
+
* pin" since R79 ("hover highlights, click to pin") but no onClick
|
|
5
|
+
* was ever wired. Same shape as R136 fix on the active-links chip.
|
|
6
|
+
*
|
|
7
|
+
* Composes with two existing pin surfaces:
|
|
8
|
+
* R60 pressure-bar segments
|
|
9
|
+
* R61 legend rows
|
|
10
|
+
* R139 working chip in the chip row (new)
|
|
11
|
+
* All three toggle the same pinnedStatus state via setPinnedStatus.
|
|
12
|
+
*
|
|
13
|
+
* Behavior tested:
|
|
14
|
+
* 1. Idle baseline: workingCount > 0, aria-pressed="false",
|
|
15
|
+
* data-pin-mirror="false", boxShadow absent
|
|
16
|
+
* 2. Click → pinnedStatus='working', aria-pressed="true",
|
|
17
|
+
* data-pin-mirror="true", boxShadow set
|
|
18
|
+
* 3. Click again → unpin, aria-pressed="false"
|
|
19
|
+
* 4. Esc clears pin (R62 invariant)
|
|
20
|
+
* 5. Empty fleet (workingCount=0): chip not interactive
|
|
21
|
+
* (no role, no tabindex, no cursor:pointer)
|
|
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
|
+
|
|
28
|
+
async function probe(workingCount, totalCount) {
|
|
29
|
+
const browser = await chromium.launch({ headless: true });
|
|
30
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
|
|
31
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
32
|
+
await ctx.addInitScript(() => {
|
|
33
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
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 sessions = [];
|
|
41
|
+
for (let i = 0; i < totalCount; i++) {
|
|
42
|
+
sessions.push({
|
|
43
|
+
alias: `node${i}`,
|
|
44
|
+
status: i < workingCount ? 'working' : 'idle',
|
|
45
|
+
model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
46
|
+
network_id: nid, project_dir: null,
|
|
47
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
51
|
+
});
|
|
52
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
53
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.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((n) => document.querySelectorAll('g[data-node]').length === n, totalCount, { timeout: 30000 });
|
|
58
|
+
await page.waitForSelector('[data-working-chip]', { timeout: 10000 });
|
|
59
|
+
await page.waitForTimeout(400);
|
|
60
|
+
|
|
61
|
+
return { browser, page };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// State A: fleet with working agents
|
|
65
|
+
const { browser: b1, page: p1 } = await probe(3, 5);
|
|
66
|
+
|
|
67
|
+
const readChip = () => p1.evaluate(() => {
|
|
68
|
+
const chip = document.querySelector('[data-working-chip]');
|
|
69
|
+
return {
|
|
70
|
+
clickable: chip?.getAttribute('data-working-chip-clickable'),
|
|
71
|
+
role: chip?.getAttribute('role'),
|
|
72
|
+
tabindex: chip?.getAttribute('tabindex'),
|
|
73
|
+
pinMirror: chip?.getAttribute('data-pin-mirror'),
|
|
74
|
+
ariaPressed: chip?.getAttribute('aria-pressed'),
|
|
75
|
+
// R180 fix: probe the box-shadow PROPERTY value, not a
|
|
76
|
+
// substring match on the style attribute. Pre-R180 the
|
|
77
|
+
// inline style only contained 'box-shadow' when pinned;
|
|
78
|
+
// R180 added 'transition: box-shadow 150ms ease-out' that
|
|
79
|
+
// also matches the substring even when unpinned. Read the
|
|
80
|
+
// actual property to keep the assertion precise.
|
|
81
|
+
boxShadow: !!(chip && chip.style && chip.style.boxShadow),
|
|
82
|
+
cursor: chip?.getAttribute('style')?.includes('cursor: pointer'),
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const before = await readChip();
|
|
87
|
+
|
|
88
|
+
await p1.locator('[data-working-chip]').click();
|
|
89
|
+
await p1.waitForTimeout(200);
|
|
90
|
+
const afterClick = await readChip();
|
|
91
|
+
|
|
92
|
+
await p1.locator('[data-working-chip]').click();
|
|
93
|
+
await p1.waitForTimeout(200);
|
|
94
|
+
const afterReclick = await readChip();
|
|
95
|
+
|
|
96
|
+
// Re-pin then test Esc clears (R62 invariant)
|
|
97
|
+
await p1.locator('[data-working-chip]').click();
|
|
98
|
+
await p1.waitForTimeout(200);
|
|
99
|
+
await p1.keyboard.press('Escape');
|
|
100
|
+
await p1.waitForTimeout(200);
|
|
101
|
+
const afterEsc = await readChip();
|
|
102
|
+
|
|
103
|
+
await b1.close();
|
|
104
|
+
|
|
105
|
+
// State B: empty fleet (workingCount=0) — chip not interactive
|
|
106
|
+
const { browser: b2, page: p2 } = await probe(0, 3);
|
|
107
|
+
const emptyState = await p2.evaluate(() => {
|
|
108
|
+
const chip = document.querySelector('[data-working-chip]');
|
|
109
|
+
return {
|
|
110
|
+
clickable: chip?.getAttribute('data-working-chip-clickable'),
|
|
111
|
+
role: chip?.getAttribute('role'),
|
|
112
|
+
tabindex: chip?.getAttribute('tabindex'),
|
|
113
|
+
cursor: chip?.getAttribute('style')?.includes('cursor: pointer'),
|
|
114
|
+
};
|
|
115
|
+
});
|
|
116
|
+
await b2.close();
|
|
117
|
+
|
|
118
|
+
const results = {
|
|
119
|
+
// Initial (3 working / 5 total)
|
|
120
|
+
before_clickableTrue: before.clickable === 'true',
|
|
121
|
+
before_roleButton: before.role === 'button',
|
|
122
|
+
before_tabIndex0: before.tabindex === '0',
|
|
123
|
+
before_ariaPressedFalse: before.ariaPressed === 'false',
|
|
124
|
+
before_pinMirrorFalse: before.pinMirror === 'false',
|
|
125
|
+
before_cursorPointer: before.cursor === true,
|
|
126
|
+
before_noBoxShadow: before.boxShadow === false,
|
|
127
|
+
|
|
128
|
+
// After click — pinned
|
|
129
|
+
click_ariaPressedTrue: afterClick.ariaPressed === 'true',
|
|
130
|
+
click_pinMirrorTrue: afterClick.pinMirror === 'true',
|
|
131
|
+
click_boxShadowSet: afterClick.boxShadow === true,
|
|
132
|
+
|
|
133
|
+
// After reclick — released
|
|
134
|
+
reclick_ariaPressedFalse: afterReclick.ariaPressed === 'false',
|
|
135
|
+
reclick_pinMirrorFalse: afterReclick.pinMirror === 'false',
|
|
136
|
+
|
|
137
|
+
// Esc clears pin
|
|
138
|
+
esc_ariaPressedFalse: afterEsc.ariaPressed === 'false',
|
|
139
|
+
esc_pinMirrorFalse: afterEsc.pinMirror === 'false',
|
|
140
|
+
|
|
141
|
+
// Empty fleet — not interactive
|
|
142
|
+
empty_clickableFalse: emptyState.clickable === 'false',
|
|
143
|
+
empty_noRole: emptyState.role === null || emptyState.role === undefined,
|
|
144
|
+
empty_noTabIndex: emptyState.tabindex === null || emptyState.tabindex === undefined,
|
|
145
|
+
empty_noCursor: !emptyState.cursor,
|
|
146
|
+
};
|
|
147
|
+
const ok = Object.values(results).every(Boolean);
|
|
148
|
+
console.log(`${ok ? '✅' : '❌'} working chip pin:`, JSON.stringify(results),
|
|
149
|
+
`\n before=`, before,
|
|
150
|
+
`\n afterClick=`, afterClick,
|
|
151
|
+
`\n afterReclick=`, afterReclick,
|
|
152
|
+
`\n afterEsc=`, afterEsc,
|
|
153
|
+
`\n emptyState=`, emptyState);
|
|
154
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/* Round 112 verification: working-status nodes get a halo opacity
|
|
2
|
+
* breath (SMIL <animate>) at 3s cycle. Idle + offline halos stay
|
|
3
|
+
* flat. reducedMotion suppresses the breath entirely.
|
|
4
|
+
*
|
|
5
|
+
* Fleet: 1 working + 1 idle + 1 offline.
|
|
6
|
+
* wkr → data-node-halo-breath="on", child <animate> present
|
|
7
|
+
* idl → data-node-halo-breath="off", no <animate> child
|
|
8
|
+
* off → data-node-halo-breath="off", no <animate> child
|
|
9
|
+
* Then probe reducedMotion=reduce → wkr also reads "off", no
|
|
10
|
+
* <animate>.
|
|
11
|
+
*/
|
|
12
|
+
import { chromium } from 'playwright';
|
|
13
|
+
import { readFileSync } from 'node:fs';
|
|
14
|
+
|
|
15
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
16
|
+
const browser = await chromium.launch({ headless: true });
|
|
17
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
18
|
+
const stale10 = new Date(Date.now() - 10 * 60 * 1000).toISOString();
|
|
19
|
+
|
|
20
|
+
const probe = async (reducedMotion) => {
|
|
21
|
+
const ctx = await browser.newContext({
|
|
22
|
+
viewport: { width: 1400, height: 1500 },
|
|
23
|
+
reducedMotion: reducedMotion ? 'reduce' : 'no-preference',
|
|
24
|
+
});
|
|
25
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
26
|
+
await ctx.addInitScript(() => {
|
|
27
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
28
|
+
});
|
|
29
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
30
|
+
const r = await route.fetch();
|
|
31
|
+
const b = await r.json();
|
|
32
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
33
|
+
const mk = (alias, status, ts) => ({
|
|
34
|
+
alias, status, model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
35
|
+
network_id: nid, project_dir: null,
|
|
36
|
+
created_at: ts, updated_at: ts, last_seen_at: ts,
|
|
37
|
+
});
|
|
38
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
39
|
+
mk('wkr', 'working', fresh),
|
|
40
|
+
mk('idl', 'idle', fresh),
|
|
41
|
+
mk('off', 'offline', stale10),
|
|
42
|
+
] } });
|
|
43
|
+
});
|
|
44
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
45
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
46
|
+
const page = await ctx.newPage();
|
|
47
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
48
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
|
|
49
|
+
await page.waitForTimeout(400);
|
|
50
|
+
const data = await page.evaluate(() => {
|
|
51
|
+
const out = {};
|
|
52
|
+
for (const a of ['wkr', 'idl', 'off']) {
|
|
53
|
+
const g = document.querySelector(`g[data-node="${a}"]`);
|
|
54
|
+
const halo = g?.querySelector('[data-node-halo-breath]');
|
|
55
|
+
out[a] = {
|
|
56
|
+
attr: halo?.getAttribute('data-node-halo-breath') || '',
|
|
57
|
+
anim: !!halo?.querySelector('animate'),
|
|
58
|
+
dur: halo?.querySelector('animate')?.getAttribute('dur') || null,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
});
|
|
63
|
+
await ctx.close();
|
|
64
|
+
return data;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const normal = await probe(false);
|
|
68
|
+
const reduced = await probe(true);
|
|
69
|
+
await browser.close();
|
|
70
|
+
|
|
71
|
+
const results = {
|
|
72
|
+
normal_wkrAttrOn: normal.wkr.attr === 'on',
|
|
73
|
+
normal_wkrHasAnimate: normal.wkr.anim === true,
|
|
74
|
+
normal_wkrDur3s: normal.wkr.dur === '3s',
|
|
75
|
+
normal_idlAttrOff: normal.idl.attr === 'off',
|
|
76
|
+
normal_idlNoAnimate: normal.idl.anim === false,
|
|
77
|
+
normal_offAttrOff: normal.off.attr === 'off',
|
|
78
|
+
normal_offNoAnimate: normal.off.anim === false,
|
|
79
|
+
reduced_wkrAttrOff: reduced.wkr.attr === 'off',
|
|
80
|
+
reduced_wkrNoAnimate: reduced.wkr.anim === false,
|
|
81
|
+
};
|
|
82
|
+
const ok = Object.values(results).every(Boolean);
|
|
83
|
+
console.log(`${ok ? '✅' : '❌'} working halo breath:`, JSON.stringify(results),
|
|
84
|
+
`\n normal=`, normal,
|
|
85
|
+
`\n reduced=`, reduced);
|
|
86
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/* Round 254 verification: three remaining theme-toggle snaps closed:
|
|
2
|
+
*
|
|
3
|
+
* 1. Top-level TopoGraph wrapper <div> — biggest theme-driven
|
|
4
|
+
* surface (whole canvas bg + border). Probe via [data-topo-svg]
|
|
5
|
+
* ancestor chain; the wrapper has ref={containerRef} but no
|
|
6
|
+
* data attr, so we look up via a known descendant.
|
|
7
|
+
* 2. Legend flow-arrow swatch <path> — added data-legend-flow-arrow.
|
|
8
|
+
* 3. Minimap container (only mounts when view is zoomed/panned) —
|
|
9
|
+
* programmatically zoom in to render minimap, then probe.
|
|
10
|
+
*
|
|
11
|
+
* Test scope:
|
|
12
|
+
* - Container has background-color + border-color transition 200ms
|
|
13
|
+
* - Flow-arrow swatch has stroke 200ms in style.transition
|
|
14
|
+
* - Minimap container has background-color + border-color + color
|
|
15
|
+
* transition 200ms
|
|
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) => ({
|
|
33
|
+
alias, status: 'working', model: 'claude-opus-4', 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'), mk('beta'), mk('gamma'), mk('delta'),
|
|
39
|
+
] } });
|
|
40
|
+
});
|
|
41
|
+
await ctx.route('**/api/hub/messages*', (r2) => r2.fulfill({ json: { messages: [] } }));
|
|
42
|
+
await ctx.route('**/api/hub/tasks*', (r2) => r2.fulfill({ json: { tasks: [] } }));
|
|
43
|
+
|
|
44
|
+
const page = await ctx.newPage();
|
|
45
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
46
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
47
|
+
await page.waitForSelector('[data-legend-flow-arrow]', { timeout: 10000 });
|
|
48
|
+
await page.waitForTimeout(300);
|
|
49
|
+
|
|
50
|
+
// Probe wrapper + flow arrow first
|
|
51
|
+
const baseline = await page.evaluate(() => {
|
|
52
|
+
const flowArrow = document.querySelector('[data-legend-flow-arrow]');
|
|
53
|
+
// Wrapper: the TopoGraph component's outer div sits as the
|
|
54
|
+
// grandparent of <svg> with the topo-panel rect inside. Use a
|
|
55
|
+
// robust climb from a known SVG element.
|
|
56
|
+
const wrapper = document.querySelector('[data-topo-wrapper]');
|
|
57
|
+
return {
|
|
58
|
+
wrapper: wrapper ? { transition: wrapper.style.transition } : null,
|
|
59
|
+
flowArrow: flowArrow ? { transition: flowArrow.style.transition } : null,
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Trigger minimap render — programmatically pan/zoom view via the
|
|
64
|
+
// reset button toggle or just dispatch a wheel event. Simpler:
|
|
65
|
+
// click the zoom-in button several times.
|
|
66
|
+
for (let i = 0; i < 3; i++) {
|
|
67
|
+
await page.locator('[data-topo-chrome-zoom-in]').click();
|
|
68
|
+
await page.waitForTimeout(120);
|
|
69
|
+
}
|
|
70
|
+
await page.waitForSelector('[data-topo-minimap]', { timeout: 5000 });
|
|
71
|
+
await page.waitForTimeout(300);
|
|
72
|
+
const minimap = await page.evaluate(() => {
|
|
73
|
+
const mm = document.querySelector('[data-topo-minimap]');
|
|
74
|
+
return mm ? { transition: mm.style.transition } : null;
|
|
75
|
+
});
|
|
76
|
+
await browser.close();
|
|
77
|
+
|
|
78
|
+
const has = (s, prop) => new RegExp(`${prop}\\s+(?:200ms|0\\.2s)`).test(s || '');
|
|
79
|
+
|
|
80
|
+
const results = {
|
|
81
|
+
wrapper_present: baseline.wrapper !== null,
|
|
82
|
+
wrapper_has_bg_color_200: has(baseline.wrapper?.transition, 'background-color'),
|
|
83
|
+
wrapper_has_border_color_200: has(baseline.wrapper?.transition, 'border-color'),
|
|
84
|
+
|
|
85
|
+
flow_arrow_present: baseline.flowArrow !== null,
|
|
86
|
+
flow_arrow_has_stroke_200: has(baseline.flowArrow?.transition, 'stroke'),
|
|
87
|
+
|
|
88
|
+
minimap_present: minimap !== null,
|
|
89
|
+
minimap_has_bg_color_200: has(minimap?.transition, 'background-color'),
|
|
90
|
+
minimap_has_border_color_200: has(minimap?.transition, 'border-color'),
|
|
91
|
+
minimap_has_color_200: has(minimap?.transition, '(?<!background-|border-)color'),
|
|
92
|
+
};
|
|
93
|
+
const ok = Object.values(results).every(Boolean);
|
|
94
|
+
console.log(`${ok ? '✅' : '❌'} wrapper + flow-arrow + minimap theme ease:`, JSON.stringify(results),
|
|
95
|
+
'\n baseline:', baseline,
|
|
96
|
+
'\n minimap:', minimap);
|
|
97
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/* Round 263 verification: wrapper box-shadow joins the theme-toggle
|
|
2
|
+
* transition list — closes R254's holdover gap.
|
|
3
|
+
*
|
|
4
|
+
* Pre-R263 the wrapper's inline transition was:
|
|
5
|
+
* transition: 'background-color 200ms ease-out, border-color 200ms ease-out'
|
|
6
|
+
*
|
|
7
|
+
* The Tailwind class `shadow-2xl ${isLight ? 'shadow-zinc-900/5'
|
|
8
|
+
* : 'shadow-cyan-950/30'}` is THEME-CONDITIONAL — on cyber→light
|
|
9
|
+
* toggle the className swap changes box-shadow color (cyan-950 30%
|
|
10
|
+
* ↔ zinc-900 5%) but the transition list didn't include box-shadow,
|
|
11
|
+
* so the shadow SNAPPED while every other theme-driven element eased.
|
|
12
|
+
*
|
|
13
|
+
* R254 claimed "TopoGraph theme-toggle smoothness TRULY complete" but
|
|
14
|
+
* missed this one surface. R263 closes it:
|
|
15
|
+
* transition: '... , box-shadow 200ms ease-out'
|
|
16
|
+
*
|
|
17
|
+
* CSS box-shadow transitions interpolate the shadow color smoothly
|
|
18
|
+
* even when the value is set indirectly via a Tailwind class — the
|
|
19
|
+
* `transition` property targets the box-shadow CSS property itself,
|
|
20
|
+
* which is transition-eligible regardless of where the new value
|
|
21
|
+
* comes from (class change vs inline style change).
|
|
22
|
+
*
|
|
23
|
+
* Test scope:
|
|
24
|
+
* 1. Wrapper present.
|
|
25
|
+
* 2. Wrapper inline transition includes box-shadow at 200ms (or 0.2s).
|
|
26
|
+
* 3. R254 invariants intact: still has background-color + border-color
|
|
27
|
+
* transitions.
|
|
28
|
+
* 4. Wrapper has a non-empty box-shadow at rest (regression — confirms
|
|
29
|
+
* the shadow class actually applies).
|
|
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'),
|
|
53
|
+
] } });
|
|
54
|
+
});
|
|
55
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
56
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
57
|
+
|
|
58
|
+
const page = await ctx.newPage();
|
|
59
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
60
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
61
|
+
await page.waitForSelector('[data-topo-wrapper]', { timeout: 10000 });
|
|
62
|
+
await page.waitForTimeout(300);
|
|
63
|
+
|
|
64
|
+
const probe = await page.evaluate(() => {
|
|
65
|
+
const w = document.querySelector('[data-topo-wrapper]');
|
|
66
|
+
return {
|
|
67
|
+
transition: w?.style.transition ?? null,
|
|
68
|
+
boxShadow: w ? window.getComputedStyle(w).boxShadow : null,
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
await browser.close();
|
|
72
|
+
|
|
73
|
+
const has = (s, prop) => new RegExp(`${prop}\\s+(?:200ms|0\\.2s)`).test(s || '');
|
|
74
|
+
|
|
75
|
+
const results = {
|
|
76
|
+
wrapper_present: probe.transition !== null,
|
|
77
|
+
transition_has_box_shadow_200: has(probe.transition, 'box-shadow'),
|
|
78
|
+
// R254 regressions
|
|
79
|
+
transition_has_bg_color_200: has(probe.transition, 'background-color'),
|
|
80
|
+
transition_has_border_color_200: has(probe.transition, 'border-color'),
|
|
81
|
+
// Wrapper actually carries a box-shadow at rest (else the transition
|
|
82
|
+
// would have nothing to interpolate). 'none' = no shadow applied.
|
|
83
|
+
wrapper_has_non_empty_shadow: probe.boxShadow !== null && probe.boxShadow !== 'none' && probe.boxShadow.length > 0,
|
|
84
|
+
};
|
|
85
|
+
const ok = Object.values(results).every(Boolean);
|
|
86
|
+
console.log(`${ok ? '✅' : '❌'} wrapper shadow theme ease:`, JSON.stringify(results),
|
|
87
|
+
'\n transition:', probe.transition,
|
|
88
|
+
'\n boxShadow:', probe.boxShadow);
|
|
89
|
+
process.exit(ok ? 0 : 1);
|