@sleep2agi/agent-network-dashboard 0.4.6-preview.22 → 0.4.6-preview.221
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/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/0i_acp5c7_kc5.js +4 -0
- package/.next/static/chunks/0j537s3zamtja.js +1 -0
- package/.next/static/chunks/0rxrez_cjn.eh.js +1 -0
- package/.next/static/chunks/0x8x.29hkju5a.js +1 -0
- package/.next/static/chunks/0~k1yvhakbob6.css +2 -0
- package/.next/static/chunks/14_8w4~_61h5q.js +1 -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 +4403 -256
- package/app/components/utils.ts +8 -7
- package/app/globals.css +110 -5
- package/app/lib/time.ts +36 -0
- package/package.json +1 -1
- 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-arrival-ping-test.mjs +87 -0
- package/scripts/topo-arrow-tier-test.mjs +61 -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-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-chrome-a11y-test.mjs +107 -0
- package/scripts/topo-chrome-press-state-test.mjs +135 -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-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-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-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-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-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-endpoint-ring-fade-test.mjs +132 -0
- package/scripts/topo-endpoint-ring-test.mjs +93 -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 +68 -0
- package/scripts/topo-group-hover-test.mjs +82 -0
- package/scripts/topo-group-label-a11y-test.mjs +113 -0
- package/scripts/topo-group-label-hover-test.mjs +119 -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-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-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-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-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-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-lift-test.mjs +119 -0
- package/scripts/topo-label-scale-test.mjs +78 -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-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-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-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-offline-dim-test.mjs +70 -0
- package/scripts/topo-online-chip-nav-test.mjs +143 -0
- package/scripts/topo-orbit-busyness-test.mjs +96 -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 +124 -0
- package/scripts/topo-panel-hover-elevation-test.mjs +118 -0
- package/scripts/topo-particle-stagger-test.mjs +82 -0
- package/scripts/topo-pin-intersection-empty-test.mjs +129 -0
- package/scripts/topo-pin-intersection-test.mjs +121 -0
- package/scripts/topo-pin-mirror-transition-test.mjs +101 -0
- package/scripts/topo-pin-persistence-test.mjs +105 -0
- package/scripts/topo-popover-meta-test.mjs +78 -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-hot-header-test.mjs +132 -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-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-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-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-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-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-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/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/0n0t~-gcuxjal.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/0ztnqa~vzxbbs.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/17jybqi.k7lrd.js +0 -1
- /package/.next/static/{Fb5M4fLP--Xb56NsSE-Bw → AuG2SpE5-8Rsg41_lUosC}/_buildManifest.js +0 -0
- /package/.next/static/{Fb5M4fLP--Xb56NsSE-Bw → AuG2SpE5-8Rsg41_lUosC}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{Fb5M4fLP--Xb56NsSE-Bw → AuG2SpE5-8Rsg41_lUosC}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/* Round 150 verification: R119 edge filter pill extends the hot-
|
|
2
|
+
* lane convention. When the pinned edge has count >= 10, the
|
|
3
|
+
* count tspan inside the pill flips to amber + 700 weight, and
|
|
4
|
+
* the tooltip grows a "hot lane · ≥ 10" marker.
|
|
5
|
+
*
|
|
6
|
+
* 4th hot-lane surface now active:
|
|
7
|
+
* R126 canvas badge → amber stroke when count >= 10
|
|
8
|
+
* R127 recent-row count → amber + bold tspan
|
|
9
|
+
* R129 panel header tail → "· N hot" amber suffix
|
|
10
|
+
* R150 edge filter pill → amber count tspan + tooltip marker
|
|
11
|
+
*
|
|
12
|
+
* All four use the same hotStroke palette (#d97706 light / #fbbf24
|
|
13
|
+
* dark) so the eye instantly recognises hot signal across
|
|
14
|
+
* surfaces.
|
|
15
|
+
*
|
|
16
|
+
* Two test states:
|
|
17
|
+
* - Pin a hot edge (count >= 10):
|
|
18
|
+
* - data-active-filter-edge-hot="true"
|
|
19
|
+
* - data-active-filter-edge-count-hot tspan present
|
|
20
|
+
* - tspan color = amber, weight = 700
|
|
21
|
+
* - title contains "hot lane · ≥ 10"
|
|
22
|
+
* - Pin a warm edge (count < 10):
|
|
23
|
+
* - data-active-filter-edge-hot="false"
|
|
24
|
+
* - data-active-filter-edge-count tspan present (non-hot)
|
|
25
|
+
* - title does NOT contain "hot lane"
|
|
26
|
+
*/
|
|
27
|
+
import { chromium } from 'playwright';
|
|
28
|
+
import { readFileSync } from 'node:fs';
|
|
29
|
+
|
|
30
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
31
|
+
const browser = await chromium.launch({ headless: true });
|
|
32
|
+
|
|
33
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
|
|
34
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
35
|
+
await ctx.addInitScript(() => {
|
|
36
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
40
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
41
|
+
const r = await route.fetch();
|
|
42
|
+
const b = await r.json();
|
|
43
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
44
|
+
const mk = (alias, status) => ({
|
|
45
|
+
alias, status, model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
46
|
+
network_id: nid, project_dir: null,
|
|
47
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
48
|
+
});
|
|
49
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
50
|
+
mk('alpha', 'working'), mk('beta', 'working'), mk('gamma', 'idle'),
|
|
51
|
+
] } });
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const now = Date.now();
|
|
55
|
+
const mkMsg = (id, from, to, ageMs) => ({
|
|
56
|
+
id, from_alias: from, to_alias: to, content: 'hi',
|
|
57
|
+
network_id: 'default', created_at: new Date(now - ageMs).toISOString(),
|
|
58
|
+
});
|
|
59
|
+
const msgs = [];
|
|
60
|
+
// 12 alpha→beta = HOT (count >= 10)
|
|
61
|
+
for (let i = 0; i < 12; i++) msgs.push(mkMsg(`a${i}`, 'alpha', 'beta', 20000 + i * 500));
|
|
62
|
+
// 4 beta→gamma = warm
|
|
63
|
+
for (let i = 0; i < 4; i++) msgs.push(mkMsg(`b${i}`, 'beta', 'gamma', 30000 + i * 500));
|
|
64
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
|
|
65
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
66
|
+
|
|
67
|
+
const page = await ctx.newPage();
|
|
68
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
69
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
|
|
70
|
+
await page.waitForSelector('[data-recent-row="alpha->beta"]', { timeout: 10000 });
|
|
71
|
+
await page.waitForTimeout(400);
|
|
72
|
+
|
|
73
|
+
const inspectPill = () => page.evaluate(() => {
|
|
74
|
+
const pill = document.querySelector('[data-active-filter="edge"]');
|
|
75
|
+
if (!pill) return null;
|
|
76
|
+
const hotTspan = pill.querySelector('[data-active-filter-edge-count-hot]');
|
|
77
|
+
const warmTspan = pill.querySelector('[data-active-filter-edge-count]');
|
|
78
|
+
return {
|
|
79
|
+
hot: pill.getAttribute('data-active-filter-edge-hot'),
|
|
80
|
+
title: pill.getAttribute('title'),
|
|
81
|
+
hotTspanText: hotTspan?.textContent?.trim(),
|
|
82
|
+
hotTspanStyle: hotTspan?.getAttribute('style'),
|
|
83
|
+
warmTspanText: warmTspan?.textContent?.trim(),
|
|
84
|
+
matchCount: pill.getAttribute('data-filter-match-count'),
|
|
85
|
+
};
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Pin hot edge alpha→beta (12 msgs)
|
|
89
|
+
await page.locator('[data-recent-row="alpha->beta"]').click();
|
|
90
|
+
await page.waitForTimeout(250);
|
|
91
|
+
const hot = await inspectPill();
|
|
92
|
+
|
|
93
|
+
// Unpin → pin warm edge beta→gamma (4 msgs)
|
|
94
|
+
await page.keyboard.press('Escape');
|
|
95
|
+
await page.waitForTimeout(200);
|
|
96
|
+
await page.locator('[data-recent-row="beta->gamma"]').click();
|
|
97
|
+
await page.waitForTimeout(250);
|
|
98
|
+
const warm = await inspectPill();
|
|
99
|
+
|
|
100
|
+
await browser.close();
|
|
101
|
+
|
|
102
|
+
// cyber theme amber #fbbf24 = rgb(251, 191, 36)
|
|
103
|
+
const amberInStyle = (s) =>
|
|
104
|
+
!!s && (/rgba?\(251,\s*191,\s*36/i.test(s) || /#fbbf24/i.test(s));
|
|
105
|
+
|
|
106
|
+
const results = {
|
|
107
|
+
// Hot edge pinned (alpha→beta, 12 msgs)
|
|
108
|
+
hot_pillPresent: hot !== null,
|
|
109
|
+
hot_attrTrue: hot?.hot === 'true',
|
|
110
|
+
hot_matchCount12: hot?.matchCount === '12',
|
|
111
|
+
hot_tspanHasCount: hot?.hotTspanText?.includes('12') === true,
|
|
112
|
+
hot_tspanAmberColor: amberInStyle(hot?.hotTspanStyle || ''),
|
|
113
|
+
hot_tspanWeight700: (hot?.hotTspanStyle || '').includes('font-weight: 700'),
|
|
114
|
+
hot_titleHotMarker: (hot?.title || '').includes('hot lane'),
|
|
115
|
+
hot_noWarmTspan: hot?.warmTspanText === undefined,
|
|
116
|
+
|
|
117
|
+
// Warm edge pinned (beta→gamma, 4 msgs)
|
|
118
|
+
warm_pillPresent: warm !== null,
|
|
119
|
+
warm_attrFalse: warm?.hot === 'false',
|
|
120
|
+
warm_matchCount4: warm?.matchCount === '4',
|
|
121
|
+
warm_tspanHasCount: warm?.warmTspanText?.includes('4') === true,
|
|
122
|
+
warm_noHotTspan: warm?.hotTspanText === undefined,
|
|
123
|
+
warm_titleNoHotMarker: !(warm?.title || '').includes('hot lane'),
|
|
124
|
+
};
|
|
125
|
+
const ok = Object.values(results).every(Boolean);
|
|
126
|
+
console.log(`${ok ? '✅' : '❌'} edge pill hot:`, JSON.stringify(results),
|
|
127
|
+
`\n hot=`, hot,
|
|
128
|
+
`\n warm=`, warm);
|
|
129
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/* Round 119 verification: when a recent-signal row is pinned (R116),
|
|
2
|
+
* the chip row renders a 4th "filter: from→to · N" pill matching
|
|
3
|
+
* the R64 / R89 pattern for status / group / vendor pins. Closes the
|
|
4
|
+
* pill-pattern gap left after R116.
|
|
5
|
+
*
|
|
6
|
+
* Verifies:
|
|
7
|
+
* - Before pin: no edge pill
|
|
8
|
+
* - Click row → pill shows correct from→to and count
|
|
9
|
+
* - Click pill body → clears
|
|
10
|
+
* - × button also clears
|
|
11
|
+
* - data-filter-match-aliases lists the two endpoints
|
|
12
|
+
*/
|
|
13
|
+
import { chromium } from 'playwright';
|
|
14
|
+
import { readFileSync } from 'node:fs';
|
|
15
|
+
|
|
16
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
17
|
+
const browser = await chromium.launch({ headless: true });
|
|
18
|
+
|
|
19
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
|
|
20
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
21
|
+
await ctx.addInitScript(() => {
|
|
22
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
26
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
27
|
+
const r = await route.fetch();
|
|
28
|
+
const b = await r.json();
|
|
29
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
30
|
+
const mk = (alias) => ({
|
|
31
|
+
alias, status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
32
|
+
network_id: nid, project_dir: null,
|
|
33
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
34
|
+
});
|
|
35
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta'), mk('gamma')] } });
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const now = Date.now();
|
|
39
|
+
const mkMsg = (id, from_alias, to_alias, ageMs) => ({
|
|
40
|
+
id, from_alias, to_alias, content: 'hi', network_id: 'default',
|
|
41
|
+
created_at: new Date(now - ageMs).toISOString(),
|
|
42
|
+
});
|
|
43
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
|
|
44
|
+
mkMsg('m1', 'alpha', 'beta', 30 * 1000),
|
|
45
|
+
mkMsg('m2', 'alpha', 'beta', 40 * 1000),
|
|
46
|
+
mkMsg('m3', 'alpha', 'beta', 50 * 1000),
|
|
47
|
+
] } }));
|
|
48
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
49
|
+
const page = await ctx.newPage();
|
|
50
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
51
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
|
|
52
|
+
await page.waitForSelector('[data-recent-row]', { timeout: 10000 });
|
|
53
|
+
await page.waitForTimeout(400);
|
|
54
|
+
|
|
55
|
+
const readPill = () => page.evaluate(() => {
|
|
56
|
+
const p = document.querySelector('[data-active-filter="edge"]');
|
|
57
|
+
if (!p) return null;
|
|
58
|
+
return {
|
|
59
|
+
text: (p.textContent || '').trim(),
|
|
60
|
+
count: p.getAttribute('data-filter-match-count'),
|
|
61
|
+
aliases: p.getAttribute('data-filter-match-aliases') || '',
|
|
62
|
+
title: p.getAttribute('title') || '',
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const before = await readPill();
|
|
67
|
+
|
|
68
|
+
// Click row to pin.
|
|
69
|
+
await page.locator('[data-recent-row="alpha->beta"]').click();
|
|
70
|
+
await page.mouse.move(10, 10);
|
|
71
|
+
await page.waitForTimeout(300);
|
|
72
|
+
const afterPin = await readPill();
|
|
73
|
+
|
|
74
|
+
// Click pill body to clear.
|
|
75
|
+
await page.locator('[data-active-filter="edge"]').click();
|
|
76
|
+
await page.mouse.move(10, 10);
|
|
77
|
+
await page.waitForTimeout(300);
|
|
78
|
+
const afterBodyClear = await readPill();
|
|
79
|
+
|
|
80
|
+
// Pin again, then click × button.
|
|
81
|
+
await page.locator('[data-recent-row="alpha->beta"]').click();
|
|
82
|
+
await page.mouse.move(10, 10);
|
|
83
|
+
await page.waitForTimeout(300);
|
|
84
|
+
await page.locator('[data-active-filter="edge"] button').click();
|
|
85
|
+
await page.waitForTimeout(300);
|
|
86
|
+
const afterXClear = await readPill();
|
|
87
|
+
|
|
88
|
+
await browser.close();
|
|
89
|
+
|
|
90
|
+
const results = {
|
|
91
|
+
before_noPill: before === null,
|
|
92
|
+
pin_pillRendered: afterPin !== null,
|
|
93
|
+
pin_textContainsArrow: /alpha→beta/.test(afterPin?.text || ''),
|
|
94
|
+
pin_count3: afterPin?.count === '3',
|
|
95
|
+
pin_aliasesMatch: afterPin?.aliases === 'alpha,beta',
|
|
96
|
+
pin_titleHasFromTo: /alpha → beta \(3 msgs\)/.test(afterPin?.title || ''),
|
|
97
|
+
bodyClear_pillGone: afterBodyClear === null,
|
|
98
|
+
xClear_pillGone: afterXClear === null,
|
|
99
|
+
};
|
|
100
|
+
const ok = Object.values(results).every(Boolean);
|
|
101
|
+
console.log(`${ok ? '✅' : '❌'} edge pill:`, JSON.stringify(results),
|
|
102
|
+
`\n afterPin=`, afterPin,
|
|
103
|
+
`\n afterBodyClear=`, afterBodyClear,
|
|
104
|
+
`\n afterXClear=`, afterXClear);
|
|
105
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/* Round 166 verification: visible flow edge path picks up
|
|
2
|
+
* stroke-width transition.
|
|
3
|
+
*
|
|
4
|
+
* R50 thickens hovered edges to 1.4× (renderWidth = width * 1.4)
|
|
5
|
+
* but pre-R166 only opacity transitioned — width snapped. R166
|
|
6
|
+
* adds stroke-width to the transition list so the edge thickens
|
|
7
|
+
* smoothly on hover, pairing with R164 edge-badge r-lift for
|
|
8
|
+
* coordinated 300ms ease-out across the edge surface.
|
|
9
|
+
*
|
|
10
|
+
* Test:
|
|
11
|
+
* 1. Mock 4 sessions + 1 flow alpha→beta
|
|
12
|
+
* 2. Probe visible path before hover → strokeWidth=base
|
|
13
|
+
* 3. Probe transition string includes both opacity and
|
|
14
|
+
* stroke-width
|
|
15
|
+
* 4. Hover edge hitbox → wait 350ms (past transition)
|
|
16
|
+
* 5. Probe strokeWidth → should be ≈ base * 1.4
|
|
17
|
+
* 6. mouse away → strokeWidth back to base
|
|
18
|
+
*/
|
|
19
|
+
import { chromium } from 'playwright';
|
|
20
|
+
import { readFileSync } from 'node:fs';
|
|
21
|
+
|
|
22
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
23
|
+
const browser = await chromium.launch({ headless: true });
|
|
24
|
+
|
|
25
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
26
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
27
|
+
await ctx.addInitScript(() => {
|
|
28
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
29
|
+
});
|
|
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) => ({
|
|
37
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
38
|
+
network_id: nid, project_dir: null,
|
|
39
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
40
|
+
});
|
|
41
|
+
// 4 sessions so alpha + beta are 90° apart (not 180° — that
|
|
42
|
+
// would put their edge midpoint on the hub, where the hub
|
|
43
|
+
// <g> intercepts pointer events). gamma + delta are filler.
|
|
44
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
45
|
+
mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
|
|
46
|
+
] } });
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const now = Date.now();
|
|
50
|
+
// 4 msgs so:
|
|
51
|
+
// - width = Math.min(2+4, 7) = 6
|
|
52
|
+
// - hover renderWidth = Math.min(6*1.4, 10) = 8.4
|
|
53
|
+
// AND the edge midpoint count badge renders (R100 threshold
|
|
54
|
+
// count >= 3) so we can use it as the hover surface. The
|
|
55
|
+
// visible path has pointerEvents:none; the R48 hitbox is a
|
|
56
|
+
// thin stroke that Playwright's .hover() can't reliably land
|
|
57
|
+
// on. The badge is a r=9 circle with pointerEvents:all and
|
|
58
|
+
// propagates hover to hoveredEdgeKey (R122) — most reliable
|
|
59
|
+
// target for triggering the visible-path transition.
|
|
60
|
+
const msgs = [];
|
|
61
|
+
for (let i = 0; i < 4; i++) {
|
|
62
|
+
msgs.push({ id: `m${i}`, from_alias: 'alpha', to_alias: 'beta', content: 'hi',
|
|
63
|
+
network_id: 'default', created_at: new Date(now - (10000 + i * 500)).toISOString() });
|
|
64
|
+
}
|
|
65
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
|
|
66
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
67
|
+
|
|
68
|
+
const page = await ctx.newPage();
|
|
69
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
70
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
71
|
+
await page.waitForSelector('[data-edge-visible]', { timeout: 10000 });
|
|
72
|
+
await page.waitForTimeout(400);
|
|
73
|
+
|
|
74
|
+
const idle = await page.evaluate(() => {
|
|
75
|
+
const el = document.querySelector('[data-edge-visible]');
|
|
76
|
+
if (!el) return null;
|
|
77
|
+
return {
|
|
78
|
+
strokeWidth: parseFloat(el.getAttribute('stroke-width')),
|
|
79
|
+
inlineTransition: el.style.transition || '',
|
|
80
|
+
computedTransition: getComputedStyle(el).transition || '',
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Hover the edge midpoint badge (R164 r=9 circle, pointerEvents:all,
|
|
85
|
+
// propagates to hoveredEdgeKey via R122). The visible path has
|
|
86
|
+
// pointerEvents:none; the R48 hitbox is a thin stroke that
|
|
87
|
+
// Playwright .hover() can't reliably land on.
|
|
88
|
+
await page.locator('[data-edge-count-badge]').hover();
|
|
89
|
+
// Wait for transition to settle.
|
|
90
|
+
await page.waitForTimeout(400);
|
|
91
|
+
const hovered = await page.evaluate(() => {
|
|
92
|
+
const el = document.querySelector('[data-edge-visible]');
|
|
93
|
+
if (!el) return null;
|
|
94
|
+
return {
|
|
95
|
+
strokeWidth: parseFloat(el.getAttribute('stroke-width')),
|
|
96
|
+
};
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Move cursor away
|
|
100
|
+
await page.mouse.move(10, 10);
|
|
101
|
+
await page.waitForTimeout(400);
|
|
102
|
+
const idleAgain = await page.evaluate(() => {
|
|
103
|
+
const el = document.querySelector('[data-edge-visible]');
|
|
104
|
+
if (!el) return null;
|
|
105
|
+
return {
|
|
106
|
+
strokeWidth: parseFloat(el.getAttribute('stroke-width')),
|
|
107
|
+
};
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
await browser.close();
|
|
111
|
+
|
|
112
|
+
const hasOpacity = (idle?.inlineTransition || '').includes('opacity 300ms') ||
|
|
113
|
+
/opacity\s+0\.3s|opacity\s+300ms/.test(idle?.computedTransition || '');
|
|
114
|
+
const hasStrokeWidth = (idle?.inlineTransition || '').includes('stroke-width 300ms') ||
|
|
115
|
+
/stroke-width\s+0\.3s|stroke-width\s+300ms/.test(idle?.computedTransition || '');
|
|
116
|
+
|
|
117
|
+
// Expected: width=Math.min(2+4, 7)=6. Hover renderWidth=Math.min(6*1.4, 10)=8.4.
|
|
118
|
+
const results = {
|
|
119
|
+
edge_found: idle !== null,
|
|
120
|
+
has_opacity_transition: hasOpacity,
|
|
121
|
+
has_stroke_width_transition: hasStrokeWidth,
|
|
122
|
+
idle_width_6: idle && Math.abs(idle.strokeWidth - 6) < 0.05,
|
|
123
|
+
hover_width_8p4: hovered && Math.abs(hovered.strokeWidth - 8.4) < 0.1,
|
|
124
|
+
hover_width_thicker: hovered && idle && hovered.strokeWidth > idle.strokeWidth,
|
|
125
|
+
idle_again_width_6: idleAgain && Math.abs(idleAgain.strokeWidth - 6) < 0.05,
|
|
126
|
+
};
|
|
127
|
+
const ok = Object.values(results).every(Boolean);
|
|
128
|
+
console.log(`${ok ? '✅' : '❌'} edge stroke transition:`, JSON.stringify(results),
|
|
129
|
+
`\n idle =`, idle,
|
|
130
|
+
`\n hovered =`, hovered,
|
|
131
|
+
`\n idleAgain =`, idleAgain);
|
|
132
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/* Round 39 verification: each flow-link path carries a `<title>` with
|
|
2
|
+
* "from → to\nN messages · last <relative>". Tests:
|
|
3
|
+
* - tooltip text shape (route + count + relative time)
|
|
4
|
+
* - singular vs. plural ("1 message" vs "5 messages")
|
|
5
|
+
* - missing last_at falls through cleanly */
|
|
6
|
+
import { chromium } from 'playwright';
|
|
7
|
+
import { readFileSync } from 'node:fs';
|
|
8
|
+
|
|
9
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
10
|
+
const browser = await chromium.launch({ headless: true });
|
|
11
|
+
|
|
12
|
+
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
|
13
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
14
|
+
await ctx.addInitScript(() => {
|
|
15
|
+
try {
|
|
16
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
17
|
+
localStorage.setItem('anet-topo-layout', 'grid');
|
|
18
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
19
|
+
} catch {}
|
|
20
|
+
});
|
|
21
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
22
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
23
|
+
const r = await route.fetch();
|
|
24
|
+
const b = await r.json();
|
|
25
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
26
|
+
const sessions = ['alpha', 'beta', 'gamma'].map(a => ({
|
|
27
|
+
alias: a, status: 'idle', network_id: nid, project_dir: null,
|
|
28
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
29
|
+
}));
|
|
30
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
31
|
+
});
|
|
32
|
+
// Inject messages: 1 between alpha→beta (singular), 5 between alpha→gamma (plural).
|
|
33
|
+
const old = new Date(Date.now() - 8 * 60 * 1000).toISOString();
|
|
34
|
+
const recent = new Date(Date.now() - 30 * 1000).toISOString();
|
|
35
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
|
|
36
|
+
{ from_alias: 'alpha', to_alias: 'beta', content: 'hello', created_at: old },
|
|
37
|
+
{ from_alias: 'alpha', to_alias: 'gamma', content: 'm1', created_at: recent },
|
|
38
|
+
{ from_alias: 'alpha', to_alias: 'gamma', content: 'm2', created_at: recent },
|
|
39
|
+
{ from_alias: 'alpha', to_alias: 'gamma', content: 'm3', created_at: recent },
|
|
40
|
+
{ from_alias: 'alpha', to_alias: 'gamma', content: 'm4', created_at: recent },
|
|
41
|
+
{ from_alias: 'alpha', to_alias: 'gamma', content: 'm5', created_at: recent },
|
|
42
|
+
] } }));
|
|
43
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
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 === 3, { timeout: 30000 });
|
|
47
|
+
await page.waitForTimeout(600);
|
|
48
|
+
|
|
49
|
+
// Collect every flow-link tooltip (each base path with a child <title>).
|
|
50
|
+
const tooltips = await page.evaluate(() => {
|
|
51
|
+
const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
|
|
52
|
+
// Two paths per flow link — the base one carries the <title>.
|
|
53
|
+
const titled = [...svg.querySelectorAll('path > title')];
|
|
54
|
+
return titled.map(t => t.textContent);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
await browser.close();
|
|
58
|
+
const has = (route, count, plural) => tooltips.some(t =>
|
|
59
|
+
t.includes(route) &&
|
|
60
|
+
(plural ? t.includes(`${count} messages`) : t.includes(`${count} message`)) &&
|
|
61
|
+
/last \w+ ago/.test(t)
|
|
62
|
+
);
|
|
63
|
+
const results = {
|
|
64
|
+
singularAlphaToBeta: has('alpha → beta', 1, false),
|
|
65
|
+
pluralAlphaToGamma: has('alpha → gamma', 5, true),
|
|
66
|
+
tooltipCount: tooltips.length === 2,
|
|
67
|
+
};
|
|
68
|
+
const ok = Object.values(results).every(Boolean);
|
|
69
|
+
console.log(`${ok ? '✅' : '❌'} edge tooltip:`, JSON.stringify(results), '\n tooltips=', tooltips);
|
|
70
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/* Round 200 verification (milestone): recent-signal panel empty-
|
|
2
|
+
* state texts breathe in opacity. The two lines "no flow yet" and
|
|
3
|
+
* "send a message between agents" each get a SMIL <animate> child
|
|
4
|
+
* cycling opacity ~0.55 ↔ 0.78 (main) and 0.36 ↔ 0.58 (sub) over
|
|
5
|
+
* 4.4s. Sub-line is phase-shifted -1.5s so the two read as
|
|
6
|
+
* inhale-exhale, not synchronised pulse.
|
|
7
|
+
*
|
|
8
|
+
* Pre-R200 both texts sat at fixed opacity 0.65 / 0.45 — visually
|
|
9
|
+
* identical to "frozen" or "broken" first-impression. Slow breath
|
|
10
|
+
* = "waiting, listening" while staying calm.
|
|
11
|
+
*
|
|
12
|
+
* Test:
|
|
13
|
+
* 1. Mock 0 messages → flowLinks.length === 0 → empty state fires.
|
|
14
|
+
* 2. data-recent-signal-empty + data-recent-signal-empty-hint
|
|
15
|
+
* both present.
|
|
16
|
+
* 3. data-recent-signal-empty-breathes='true'
|
|
17
|
+
* data-recent-signal-empty-hint-breathes='true'
|
|
18
|
+
* 4. Both texts contain an <animate> child with:
|
|
19
|
+
* - attributeName='opacity'
|
|
20
|
+
* - dur='4.4s'
|
|
21
|
+
* - repeatCount='indefinite'
|
|
22
|
+
* - values cover a range (not a single value)
|
|
23
|
+
* 5. Sub-line <animate> has begin='-1.5s' (phase-shift).
|
|
24
|
+
* 6. Main <animate> begin is unset / '0s' (no phase shift).
|
|
25
|
+
* 7. Captured opacity values shift over time (sample twice, ~2s
|
|
26
|
+
* apart): same element should produce different rendered
|
|
27
|
+
* opacity values because SMIL is animating.
|
|
28
|
+
*/
|
|
29
|
+
import { chromium } from 'playwright';
|
|
30
|
+
import { readFileSync } from 'node:fs';
|
|
31
|
+
|
|
32
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
33
|
+
const browser = await chromium.launch({ headless: true });
|
|
34
|
+
|
|
35
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
36
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
37
|
+
await ctx.addInitScript(() => {
|
|
38
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
42
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
43
|
+
const r = await route.fetch();
|
|
44
|
+
const b = await r.json();
|
|
45
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
46
|
+
const mk = (alias) => ({
|
|
47
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
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
|
+
// 0 messages → empty state fires
|
|
56
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
57
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
58
|
+
|
|
59
|
+
const page = await ctx.newPage();
|
|
60
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
61
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
62
|
+
await page.waitForSelector('[data-recent-signal-empty]', { timeout: 10000 });
|
|
63
|
+
await page.waitForTimeout(300);
|
|
64
|
+
|
|
65
|
+
const probeStructure = async () => page.evaluate(() => {
|
|
66
|
+
const main = document.querySelector('[data-recent-signal-empty]');
|
|
67
|
+
const sub = document.querySelector('[data-recent-signal-empty-hint]');
|
|
68
|
+
function pickAnim(textEl) {
|
|
69
|
+
if (!textEl) return null;
|
|
70
|
+
const a = textEl.querySelector('animate');
|
|
71
|
+
if (!a) return { found: false };
|
|
72
|
+
return {
|
|
73
|
+
found: true,
|
|
74
|
+
attribute: a.getAttribute('attributeName'),
|
|
75
|
+
values: a.getAttribute('values'),
|
|
76
|
+
dur: a.getAttribute('dur'),
|
|
77
|
+
begin: a.getAttribute('begin'),
|
|
78
|
+
repeatCount: a.getAttribute('repeatCount'),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
mainText: main?.textContent?.trim(),
|
|
83
|
+
mainBreathes:main?.getAttribute('data-recent-signal-empty-breathes'),
|
|
84
|
+
mainAnim: pickAnim(main),
|
|
85
|
+
subText: sub?.textContent?.trim(),
|
|
86
|
+
subBreathes: sub?.getAttribute('data-recent-signal-empty-hint-breathes'),
|
|
87
|
+
subAnim: pickAnim(sub),
|
|
88
|
+
};
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const s = await probeStructure();
|
|
92
|
+
|
|
93
|
+
// Sample rendered opacity twice ~2s apart to verify SMIL is actually
|
|
94
|
+
// animating in the browser, not just declared.
|
|
95
|
+
async function sampleOpacities() {
|
|
96
|
+
return page.evaluate(() => {
|
|
97
|
+
const main = document.querySelector('[data-recent-signal-empty]');
|
|
98
|
+
const sub = document.querySelector('[data-recent-signal-empty-hint]');
|
|
99
|
+
// SMIL renders into the SVG-internal opacity (DOM attr stays
|
|
100
|
+
// static at the React-rendered value; computed style reads the
|
|
101
|
+
// animated current value).
|
|
102
|
+
return {
|
|
103
|
+
mainComputed: parseFloat(getComputedStyle(main).opacity),
|
|
104
|
+
subComputed: parseFloat(getComputedStyle(sub).opacity),
|
|
105
|
+
};
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
const sample1 = await sampleOpacities();
|
|
109
|
+
await page.waitForTimeout(2000);
|
|
110
|
+
const sample2 = await sampleOpacities();
|
|
111
|
+
|
|
112
|
+
await browser.close();
|
|
113
|
+
|
|
114
|
+
const parseValues = (v) => (v || '').split(/[;,]/).map(parseFloat).filter(Number.isFinite);
|
|
115
|
+
const mainVals = parseValues(s.mainAnim?.values);
|
|
116
|
+
const subVals = parseValues(s.subAnim?.values);
|
|
117
|
+
|
|
118
|
+
const results = {
|
|
119
|
+
empty_state_visible: s.mainText === 'no flow yet',
|
|
120
|
+
hint_visible: s.subText === 'send a message between agents',
|
|
121
|
+
main_breathes_attr: s.mainBreathes === 'true',
|
|
122
|
+
hint_breathes_attr: s.subBreathes === 'true',
|
|
123
|
+
|
|
124
|
+
main_anim_found: s.mainAnim?.found === true,
|
|
125
|
+
main_anim_opacity: s.mainAnim?.attribute === 'opacity',
|
|
126
|
+
main_anim_dur_4p4s: s.mainAnim?.dur === '4.4s',
|
|
127
|
+
main_anim_indefinite: s.mainAnim?.repeatCount === 'indefinite',
|
|
128
|
+
main_anim_values_range: mainVals.length >= 3 && (Math.max(...mainVals) - Math.min(...mainVals)) > 0.1,
|
|
129
|
+
main_anim_no_phase_shift: !s.mainAnim?.begin || s.mainAnim?.begin === '0s' || s.mainAnim?.begin === '',
|
|
130
|
+
|
|
131
|
+
sub_anim_found: s.subAnim?.found === true,
|
|
132
|
+
sub_anim_opacity: s.subAnim?.attribute === 'opacity',
|
|
133
|
+
sub_anim_dur_4p4s: s.subAnim?.dur === '4.4s',
|
|
134
|
+
sub_anim_phase_shift: s.subAnim?.begin === '-1.5s',
|
|
135
|
+
sub_anim_values_range: subVals.length >= 3 && (Math.max(...subVals) - Math.min(...subVals)) > 0.1,
|
|
136
|
+
|
|
137
|
+
// SMIL actually animates — sampling at different cycle phases yields
|
|
138
|
+
// different rendered opacities. With 4.4s period and 2s gap, the two
|
|
139
|
+
// samples are at distinct phases and should diverge by at least 0.04
|
|
140
|
+
// unless we landed on symmetric cycle points (extremely rare given
|
|
141
|
+
// mock timing variance — re-check loose bound).
|
|
142
|
+
main_opacity_animating: Math.abs(sample1.mainComputed - sample2.mainComputed) > 0.02,
|
|
143
|
+
sub_opacity_animating: Math.abs(sample1.subComputed - sample2.subComputed) > 0.02,
|
|
144
|
+
};
|
|
145
|
+
const ok = Object.values(results).every(Boolean);
|
|
146
|
+
console.log(`${ok ? '✅' : '❌'} empty-state breath:`, JSON.stringify(results),
|
|
147
|
+
`\n structure:`, s,
|
|
148
|
+
`\n sample1:`, sample1,
|
|
149
|
+
`\n sample2:`, sample2);
|
|
150
|
+
process.exit(ok ? 0 : 1);
|