@sleep2agi/agent-network-dashboard 0.5.2-preview.0 → 0.5.2-preview.10

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.
Files changed (154) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +3 -3
  3. package/.next/diagnostics/route-bundle-stats.json +7 -7
  4. package/.next/fallback-build-manifest.json +3 -3
  5. package/.next/server/app/_global-error.html +1 -1
  6. package/.next/server/app/_global-error.rsc +1 -1
  7. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  8. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  9. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  12. package/.next/server/app/_not-found.html +1 -1
  13. package/.next/server/app/_not-found.rsc +1 -1
  14. package/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  15. package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  16. package/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  17. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  18. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  19. package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  20. package/.next/server/app/admin.html +1 -1
  21. package/.next/server/app/admin.rsc +1 -1
  22. package/.next/server/app/admin.segments/_full.segment.rsc +1 -1
  23. package/.next/server/app/admin.segments/_head.segment.rsc +1 -1
  24. package/.next/server/app/admin.segments/_index.segment.rsc +1 -1
  25. package/.next/server/app/admin.segments/_tree.segment.rsc +1 -1
  26. package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +1 -1
  27. package/.next/server/app/admin.segments/admin.segment.rsc +1 -1
  28. package/.next/server/app/index.html +2 -2
  29. package/.next/server/app/index.rsc +2 -2
  30. package/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  31. package/.next/server/app/index.segments/_full.segment.rsc +2 -2
  32. package/.next/server/app/index.segments/_head.segment.rsc +1 -1
  33. package/.next/server/app/index.segments/_index.segment.rsc +1 -1
  34. package/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  35. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  36. package/.next/server/app/login.html +2 -2
  37. package/.next/server/app/login.rsc +2 -2
  38. package/.next/server/app/login.segments/_full.segment.rsc +2 -2
  39. package/.next/server/app/login.segments/_head.segment.rsc +1 -1
  40. package/.next/server/app/login.segments/_index.segment.rsc +1 -1
  41. package/.next/server/app/login.segments/_tree.segment.rsc +1 -1
  42. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +2 -2
  43. package/.next/server/app/login.segments/login.segment.rsc +1 -1
  44. package/.next/server/app/logs.html +1 -1
  45. package/.next/server/app/logs.rsc +1 -1
  46. package/.next/server/app/logs.segments/_full.segment.rsc +1 -1
  47. package/.next/server/app/logs.segments/_head.segment.rsc +1 -1
  48. package/.next/server/app/logs.segments/_index.segment.rsc +1 -1
  49. package/.next/server/app/logs.segments/_tree.segment.rsc +1 -1
  50. package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +1 -1
  51. package/.next/server/app/logs.segments/logs.segment.rsc +1 -1
  52. package/.next/server/app/messages.html +1 -1
  53. package/.next/server/app/messages.rsc +1 -1
  54. package/.next/server/app/messages.segments/_full.segment.rsc +1 -1
  55. package/.next/server/app/messages.segments/_head.segment.rsc +1 -1
  56. package/.next/server/app/messages.segments/_index.segment.rsc +1 -1
  57. package/.next/server/app/messages.segments/_tree.segment.rsc +1 -1
  58. package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +1 -1
  59. package/.next/server/app/messages.segments/messages.segment.rsc +1 -1
  60. package/.next/server/app/node.html +1 -1
  61. package/.next/server/app/node.rsc +1 -1
  62. package/.next/server/app/node.segments/_full.segment.rsc +1 -1
  63. package/.next/server/app/node.segments/_head.segment.rsc +1 -1
  64. package/.next/server/app/node.segments/_index.segment.rsc +1 -1
  65. package/.next/server/app/node.segments/_tree.segment.rsc +1 -1
  66. package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +1 -1
  67. package/.next/server/app/node.segments/node.segment.rsc +1 -1
  68. package/.next/server/app/nodes.html +1 -1
  69. package/.next/server/app/nodes.rsc +1 -1
  70. package/.next/server/app/nodes.segments/_full.segment.rsc +1 -1
  71. package/.next/server/app/nodes.segments/_head.segment.rsc +1 -1
  72. package/.next/server/app/nodes.segments/_index.segment.rsc +1 -1
  73. package/.next/server/app/nodes.segments/_tree.segment.rsc +1 -1
  74. package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +1 -1
  75. package/.next/server/app/nodes.segments/nodes.segment.rsc +1 -1
  76. package/.next/server/app/page_client-reference-manifest.js +1 -1
  77. package/.next/server/app/server-logs.html +1 -1
  78. package/.next/server/app/server-logs.rsc +1 -1
  79. package/.next/server/app/server-logs.segments/_full.segment.rsc +1 -1
  80. package/.next/server/app/server-logs.segments/_head.segment.rsc +1 -1
  81. package/.next/server/app/server-logs.segments/_index.segment.rsc +1 -1
  82. package/.next/server/app/server-logs.segments/_tree.segment.rsc +1 -1
  83. package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +1 -1
  84. package/.next/server/app/server-logs.segments/server-logs.segment.rsc +1 -1
  85. package/.next/server/app/settings/networks.html +1 -1
  86. package/.next/server/app/settings/networks.rsc +1 -1
  87. package/.next/server/app/settings/networks.segments/_full.segment.rsc +1 -1
  88. package/.next/server/app/settings/networks.segments/_head.segment.rsc +1 -1
  89. package/.next/server/app/settings/networks.segments/_index.segment.rsc +1 -1
  90. package/.next/server/app/settings/networks.segments/_tree.segment.rsc +1 -1
  91. package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +1 -1
  92. package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +1 -1
  93. package/.next/server/app/settings/networks.segments/settings.segment.rsc +1 -1
  94. package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  95. package/.next/server/app/settings/tokens.html +1 -1
  96. package/.next/server/app/settings/tokens.rsc +1 -1
  97. package/.next/server/app/settings/tokens.segments/_full.segment.rsc +1 -1
  98. package/.next/server/app/settings/tokens.segments/_head.segment.rsc +1 -1
  99. package/.next/server/app/settings/tokens.segments/_index.segment.rsc +1 -1
  100. package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +1 -1
  101. package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +1 -1
  102. package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +1 -1
  103. package/.next/server/app/settings/tokens.segments/settings.segment.rsc +1 -1
  104. package/.next/server/app/settings.html +2 -2
  105. package/.next/server/app/settings.rsc +2 -2
  106. package/.next/server/app/settings.segments/_full.segment.rsc +2 -2
  107. package/.next/server/app/settings.segments/_head.segment.rsc +1 -1
  108. package/.next/server/app/settings.segments/_index.segment.rsc +1 -1
  109. package/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
  110. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +2 -2
  111. package/.next/server/app/settings.segments/settings.segment.rsc +1 -1
  112. package/.next/server/app/tasks.html +1 -1
  113. package/.next/server/app/tasks.rsc +1 -1
  114. package/.next/server/app/tasks.segments/_full.segment.rsc +1 -1
  115. package/.next/server/app/tasks.segments/_head.segment.rsc +1 -1
  116. package/.next/server/app/tasks.segments/_index.segment.rsc +1 -1
  117. package/.next/server/app/tasks.segments/_tree.segment.rsc +1 -1
  118. package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +1 -1
  119. package/.next/server/app/tasks.segments/tasks.segment.rsc +1 -1
  120. package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js +1 -1
  121. package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js.map +1 -1
  122. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +2 -2
  123. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
  124. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
  125. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
  126. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
  127. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
  128. package/.next/server/middleware-build-manifest.js +3 -3
  129. package/.next/server/pages/404.html +1 -1
  130. package/.next/server/pages/500.html +1 -1
  131. package/.next/static/chunks/0tio1iob9rlrz.js +1 -0
  132. package/.next/static/chunks/0xkn4hstf40ax.js +1 -0
  133. package/.next/static/chunks/{0p8xwrfjtykvn.js → 0z3uxdpbxx6vi.js} +1 -1
  134. package/.next/static/chunks/0z_jx0znc~hgn.js +4 -0
  135. package/.next/trace +2 -2
  136. package/.next/trace-build +1 -1
  137. package/app/components/TopoGraph.tsx +230 -10
  138. package/package.json +1 -1
  139. package/scripts/topo-group-fade-cadence-test.mjs +92 -0
  140. package/scripts/topo-group-label-glow-test.mjs +104 -0
  141. package/scripts/topo-hub-digit-glow-test.mjs +105 -0
  142. package/scripts/topo-layout-theme-attrs-test.mjs +91 -0
  143. package/scripts/topo-legend-pin-ring-glow-test.mjs +105 -0
  144. package/scripts/topo-legend-row-text-cadence-test.mjs +93 -0
  145. package/scripts/topo-legend-row-tint-cadence-test.mjs +92 -0
  146. package/scripts/topo-recent-row-freshness-glow-test.mjs +100 -0
  147. package/scripts/topo-recent-row-text-cadence-test.mjs +99 -0
  148. package/scripts/topo-recent-row-tint-cadence-test.mjs +96 -0
  149. package/.next/static/chunks/0caxil0dw-oe9.js +0 -4
  150. package/.next/static/chunks/0x.m3vy8e5iit.js +0 -1
  151. package/.next/static/chunks/12gq1w9k_7v06.js +0 -1
  152. /package/.next/static/{5IMzNtH5S5Oqe-FCw1CX4 → IKi4kxrMEeLozKLpLTtI7}/_buildManifest.js +0 -0
  153. /package/.next/static/{5IMzNtH5S5Oqe-FCw1CX4 → IKi4kxrMEeLozKLpLTtI7}/_clientMiddlewareManifest.js +0 -0
  154. /package/.next/static/{5IMzNtH5S5Oqe-FCw1CX4 → IKi4kxrMEeLozKLpLTtI7}/_ssgManifest.js +0 -0
package/.next/trace-build CHANGED
@@ -1 +1 @@
1
- [{"name":"run-turbopack","duration":7290010,"timestamp":176329576850,"id":14,"parentId":1,"tags":{},"startTime":1778984301823,"traceId":"64d36f3e62fb7597"},{"name":"turbopack-build-events","duration":116,"timestamp":176329934689,"id":15,"parentId":1,"tags":{},"startTime":1778984302181,"traceId":"64d36f3e62fb7597"},{"name":"run-typescript","duration":10461921,"timestamp":176336876132,"id":16,"parentId":1,"tags":{},"startTime":1778984309122,"traceId":"64d36f3e62fb7597"},{"name":"static-check","duration":870136,"timestamp":176347547833,"id":19,"parentId":1,"tags":{},"startTime":1778984319794,"traceId":"64d36f3e62fb7597"},{"name":"static-generation","duration":622100,"timestamp":176348435521,"id":109,"parentId":1,"tags":{},"startTime":1778984320682,"traceId":"64d36f3e62fb7597"},{"name":"telemetry-flush","duration":25650,"timestamp":176349095910,"id":118,"parentId":1,"tags":{},"startTime":1778984321342,"traceId":"64d36f3e62fb7597"},{"name":"next-build","duration":19793552,"timestamp":176329328036,"id":1,"tags":{"buildMode":"default","version":"16.2.3","bundler":"turbopack","has-custom-webpack-config":"false","use-build-worker":"true"},"startTime":1778984301574,"traceId":"64d36f3e62fb7597"}]
1
+ [{"name":"run-turbopack","duration":7066537,"timestamp":180466032127,"id":14,"parentId":1,"tags":{},"startTime":1778988438278,"traceId":"2c9b0a2821672176"},{"name":"turbopack-build-events","duration":111,"timestamp":180466373402,"id":15,"parentId":1,"tags":{},"startTime":1778988438620,"traceId":"2c9b0a2821672176"},{"name":"run-typescript","duration":10551465,"timestamp":180473107342,"id":16,"parentId":1,"tags":{},"startTime":1778988445354,"traceId":"2c9b0a2821672176"},{"name":"static-check","duration":849794,"timestamp":180483841042,"id":19,"parentId":1,"tags":{},"startTime":1778988456087,"traceId":"2c9b0a2821672176"},{"name":"static-generation","duration":667122,"timestamp":180484705909,"id":109,"parentId":1,"tags":{},"startTime":1778988456952,"traceId":"2c9b0a2821672176"},{"name":"telemetry-flush","duration":19279,"timestamp":180485406138,"id":118,"parentId":1,"tags":{},"startTime":1778988457652,"traceId":"2c9b0a2821672176"},{"name":"next-build","duration":19628296,"timestamp":180465797163,"id":1,"tags":{"buildMode":"default","version":"16.2.3","bundler":"turbopack","has-custom-webpack-config":"false","use-build-worker":"true"},"startTime":1778988438043,"traceId":"2c9b0a2821672176"}]
@@ -3485,6 +3485,25 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
3485
3485
  data-topo-working-count={workingCount}
3486
3486
  data-topo-offline-count={offlineNodes.length}
3487
3487
  data-topo-flow-count={flowLinks.length}
3488
+ /* Round 471 / Loop — surface 2 remaining canvas-level mode
3489
+ attrs alongside the R462/R466/R467/R469 set. Pre-R471 the
3490
+ root svg exposed 7 attrs but tests probing "what layout
3491
+ is active" had to query DOM internals (data-topo-chrome-
3492
+ layout-active on the chrome button row) or parse the URL
3493
+ for theme. R471 puts both modes on the root for one-stop
3494
+ snapshot reads:
3495
+ data-topo-layout — 'ring' | 'grid'
3496
+ data-topo-theme — 'cyber' | 'light'
3497
+ Together with R469 the canvas root now carries 9 cross-
3498
+ cutting attrs (1 build identity + 2 inspection mode + 4
3499
+ fleet split + 2 layout/theme). Test harness can read the
3500
+ FULL canvas state with 9 getAttribute calls; no traversal
3501
+ into chrome strip / theme provider / panel rows.
3502
+ Composed from existing `layout` (R138 ring↔grid toggle
3503
+ state) + `isLight` (R12 theme palette gate) — no new
3504
+ state, zero re-render cost. */
3505
+ data-topo-layout={layout}
3506
+ data-topo-theme={isLight ? 'light' : 'cyber'}
3488
3507
  /* Round 466 / Loop — aggregate hover signal on the root SVG.
3489
3508
  Exposes a single boolean `data-topo-any-hover` that
3490
3509
  reflects whether ANY hover state in the topology is
@@ -4347,8 +4366,29 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
4347
4366
  // style below (1 / 0.28 based on filter pin state).
4348
4367
  // data-group-fade-delay exposes the computed delay for
4349
4368
  // test probes.
4350
- className="transition-opacity anet-fade-in"
4369
+ /* Round 470 / Loop — sync the R8 out-of-focus dim
4370
+ transition cadence from Tailwind's `transition-
4371
+ opacity` default (150ms ease-in-out) to 200ms
4372
+ ease-out to match the rest of the cluster's
4373
+ motion vocabulary. Hero D #147 stack established
4374
+ 200ms ease-out across every cluster axis:
4375
+ parent text (codex p.125)
4376
+ parent rect (R461 xywh + R464 rx + R248 paint)
4377
+ hitbox rect (R459 fill+opacity + R460 x+width
4378
+ + R465 rx)
4379
+ The wrapper <g>'s opacity flip (1 → 0.28 when
4380
+ another group is active) was the LAST surface
4381
+ still at 150ms — when the user hovers a group
4382
+ label, out-of-focus groups dimmed 50ms faster
4383
+ than the focused group's tint brightened, a
4384
+ small but perceivable rate-desync. R470 lifts
4385
+ the wrapper to 200ms ease-out. duration-200 +
4386
+ ease-out are Tailwind v4 utility classes; the
4387
+ anet-fade-in mount-once keyframe stays in the
4388
+ className for first-paint stagger (R173). */
4389
+ className="transition-opacity duration-200 ease-out anet-fade-in"
4351
4390
  data-group-fade-delay={Math.min(boxIdx, 8) * 60}
4391
+ data-group-fade-transition="200ms"
4352
4392
  // R63: drop the blanket pointerEvents:'none' that
4353
4393
  // previously sat here. Chrome's SVG impl doesn't let a
4354
4394
  // child override a parent's `none` even though the spec
@@ -4721,10 +4761,35 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
4721
4761
  opacity={isPinned || isHovered ? 1 : 0.55}
4722
4762
  data-group-label-hovered={isHovered && !isPinned ? 'true' : 'false'}
4723
4763
  data-group-label-font-weight={isPinned ? '800' : '700'}
4764
+ /* Round 479 / Loop — extend drop-shadow visual-polish
4765
+ family to a 4th anchor: group-label parent text
4766
+ on isPinned. Continues the R476/R477/R478 arc:
4767
+ R476 hub digit hover-gated emerald
4768
+ R477 legend pin-ring pin-gated row.fill
4769
+ R478 recent-row pip freshness-gated cyan
4770
+ R479 group-label text pin-gated cyan
4771
+ Hue: pal.legendAccent at 0x80 alpha (≈50%) — same
4772
+ accent family R107/R477 use for tint surfaces. 3px
4773
+ blur reads as a soft cyan halo around the locked
4774
+ cluster name. Stacks with the R432 letter-spacing
4775
+ spread + R457 fw lift + R63 fill brighten + R142
4776
+ drop-shadow on the parent rect — pin signature on
4777
+ group label scope now spans typography + chroma +
4778
+ paint + container-lift + text-glow.
4779
+ Filter is paint-only; bbox unchanged; overlap-test
4780
+ invariants hold (R51 selector gated to g[data-node]
4781
+ descendants, this label is invisible to the probe).
4782
+ transition list extends to include 'filter 200ms
4783
+ ease-out' alongside the existing fill/ls/fw/opacity
4784
+ 200ms tweens. */
4785
+ data-group-label-glow={isPinned ? 'true' : 'false'}
4724
4786
  style={{
4725
- transition: 'fill 200ms ease-out, letter-spacing 200ms ease-out, font-weight 200ms ease-out, opacity 200ms ease-out',
4787
+ transition: 'fill 200ms ease-out, letter-spacing 200ms ease-out, font-weight 200ms ease-out, opacity 200ms ease-out, filter 200ms ease-out',
4726
4788
  letterSpacing: isPinned ? '0.5px' :
4727
4789
  isHovered ? '0.25px' : '0px',
4790
+ filter: isPinned
4791
+ ? `drop-shadow(0 0 3px ${pal.legendAccent}80)`
4792
+ : undefined,
4728
4793
  }}
4729
4794
  data-group-label={box.key}
4730
4795
  data-group-label-pinned={isPinned ? 'true' : 'false'}
@@ -6263,15 +6328,45 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
6263
6328
  /* Round 253 / Loop: append fill 200ms to the hub
6264
6329
  digit transition list — theme toggle (cyber #ecfdf5
6265
6330
  ↔ light #d1fae5) was the last hub-area snap. */
6331
+ /* Round 476 / Loop — hub working-count digit gains a
6332
+ filter: drop-shadow glow on hub-hover. Stacks with
6333
+ the existing 4-axis hub-hover gesture stack on this
6334
+ element:
6335
+ R209 transform: scale(1.08) geometry
6336
+ R425 fontWeight 700 → 800 typography
6337
+ R253 fill ease-out chroma (theme)
6338
+ R213 opacity gate fade (count cross)
6339
+ R476 filter drop-shadow glow paint (this round)
6340
+ The glow uses the cyber emerald-400 (#34d399) /
6341
+ light emerald-500 (#10b981) hue family so the
6342
+ chroma stays inside the hub-area palette. Subtle
6343
+ 2-3 px blur radius at 0.6 opacity — visible but
6344
+ not loud, reads as "the focal digit lit up under
6345
+ attention".
6346
+ Reduced-motion users skip the filter via the
6347
+ !reducedMotion gate (R29 a11y blanket).
6348
+ Filter is a paint-only attribute — bbox stays
6349
+ the same, R51 overlap-test invariants hold.
6350
+ transition list extends to 'filter 200ms ease-out'
6351
+ so the glow eases under the same cadence as the
6352
+ scale + fw + fill axes. */
6353
+ data-topo-hub-working-count-glow={!reducedMotion && hoveredHub ? 'true' : 'false'}
6266
6354
  style={{
6267
6355
  pointerEvents: 'none',
6268
6356
  transform: !reducedMotion && hoveredHub ? 'scale(1.08)' : 'scale(1)',
6269
6357
  transformBox: 'fill-box',
6270
6358
  transformOrigin: 'center',
6359
+ filter: !reducedMotion && hoveredHub
6360
+ ? (isLight
6361
+ ? 'drop-shadow(0 0 2px rgba(16, 185, 129, 0.6))'
6362
+ : 'drop-shadow(0 0 3px rgba(52, 211, 153, 0.6))')
6363
+ : undefined,
6271
6364
  /* R425: font-weight 200ms appended so the hover fw
6272
6365
  bump 700 → 800 eases under the same cadence as
6273
- R209 scale + R253 fill + R213 opacity. */
6274
- transition: 'transform 200ms ease-out, opacity 300ms ease-out, fill 200ms ease-out, font-weight 200ms ease-out',
6366
+ R209 scale + R253 fill + R213 opacity.
6367
+ R476: filter 200ms appended so the new drop-
6368
+ shadow glow eases at the same cadence. */
6369
+ transition: 'transform 200ms ease-out, opacity 300ms ease-out, fill 200ms ease-out, font-weight 200ms ease-out, filter 200ms ease-out',
6275
6370
  fontVariantNumeric: 'tabular-nums',
6276
6371
  }}
6277
6372
  >
@@ -8785,6 +8880,22 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
8785
8880
  the chip-row pills. R116: pinned rows tint
8786
8881
  stronger than hovered ones so locked vs preview
8787
8882
  is discriminable. */}
8883
+ {/* Round 472 / Loop — cadence-sync follow-on to the
8884
+ R459/R460/R461/R464/R465/R470 200ms uniform
8885
+ motion stack established at the cluster scope.
8886
+ This R104 recent-signal row tint rect was still
8887
+ at the legacy 150ms cadence — when a user
8888
+ hovers/pins a recent-signal row, the tint
8889
+ snapped in 50ms ahead of the rest of the row's
8890
+ state-change cascade (R143 translateY,
8891
+ R220+R434 letter-spacing, R434 fill tween).
8892
+ R472 lifts to 200ms ease-out to match. Same
8893
+ sibling idiom R459 closed at the group-label
8894
+ hitbox tier; now applied at the recent-signal
8895
+ row tier. data-recent-row-tint-transition attr
8896
+ exposes the cadence for tests.
8897
+ Geometry/paint logic unchanged — purely the
8898
+ transition timing. */}
8788
8899
  <rect
8789
8900
  x="6" y={38 + index * 16 - 10}
8790
8901
  width="218" height="14" rx="3"
@@ -8792,7 +8903,9 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
8792
8903
  opacity={isRowPinned ? (isLight ? 0.18 : 0.22)
8793
8904
  : isRowHovered ? (isLight ? 0.10 : 0.14)
8794
8905
  : 1}
8795
- style={{ transition: 'fill 150ms ease-out, opacity 150ms ease-out' }}
8906
+ data-recent-row-tint={link.key}
8907
+ data-recent-row-tint-transition="200ms"
8908
+ style={{ transition: 'fill 200ms ease-out, opacity 200ms ease-out' }}
8796
8909
  />
8797
8910
  {/* Round 160 / Loop: recency pip. Canvas flow edges
8798
8911
  fade by freshness (R10: full intensity ≤30s →
@@ -8894,16 +9007,47 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
8894
9007
  to include 'r 200ms ease-out' matching the
8895
9008
  opacity cadence. data-recent-row-freshness-
8896
9009
  lifted attr exposes the gate for tests. */
9010
+ /* Round 478 / Loop — extend the R476/R477
9011
+ drop-shadow vocabulary to a third anchor:
9012
+ the recent-row freshness pip on `alpha
9013
+ > 0.7` (just-fired flow within ~30s per
9014
+ R10 freshness ramp). Gate is FRESHNESS-
9015
+ driven not pin/hover-driven, so the glow
9016
+ reads as "this signal is live" rather
9017
+ than "user is inspecting". As the alpha
9018
+ decays past 0.7 (≈45s after last fire),
9019
+ the glow eases off — natural breathing
9020
+ feel that tracks actual data freshness.
9021
+ Hue: pal.legendAccent at 0.5 alpha so
9022
+ the glow inherits the row's accent color
9023
+ family. 2.5-3px blur reads as soft
9024
+ radiance, not loud bloom.
9025
+ Drop-shadow visual-polish family now 3
9026
+ anchors:
9027
+ R476 hub digit hover-gated
9028
+ R477 legend pin-ring pin-gated
9029
+ R478 recent freshness freshness-gated
9030
+ Each anchor uses a different state gate
9031
+ but the same `filter: drop-shadow` paint
9032
+ vocabulary. Filter affects paint only —
9033
+ bbox unchanged, overlap-test invariants
9034
+ hold. Transition list extends to include
9035
+ 'filter 200ms ease-out' alongside
9036
+ R10/R447 opacity + r tweens. */
8897
9037
  fill={pal.legendAccent}
8898
9038
  opacity={alpha}
8899
9039
  data-recent-row-freshness={link.key}
8900
9040
  data-recent-row-freshness-alpha={alpha.toFixed(2)}
8901
9041
  data-recent-row-freshness-radius={(isRowHovered || isRowPinned) ? 2.5 : 2.0}
8902
9042
  data-recent-row-freshness-lifted={(isRowHovered || isRowPinned) ? 'true' : 'false'}
9043
+ data-recent-row-freshness-glow={alpha > 0.7 ? 'true' : 'false'}
8903
9044
  style={{
8904
9045
  pointerEvents: 'none',
8905
9046
  r: `${(isRowHovered || isRowPinned) ? 2.5 : 2.0}px`,
8906
- transition: 'opacity 200ms ease-out, r 200ms ease-out',
9047
+ filter: alpha > 0.7
9048
+ ? `drop-shadow(0 0 3px ${pal.legendAccent}80)`
9049
+ : undefined,
9050
+ transition: 'opacity 200ms ease-out, r 200ms ease-out, filter 200ms ease-out',
8907
9051
  } as React.CSSProperties}
8908
9052
  />
8909
9053
  );
@@ -8988,8 +9132,25 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
8988
9132
  R427/R431/R432/R433/R434. R55 fill 150ms +
8989
9133
  R220 letter-spacing 150ms transition kept
8990
9134
  (additive conditional case, no new property). */
9135
+ /* Round 474 / Loop — cadence-sync follow-on to
9136
+ R472. R472 lifted the recent-row TINT RECT
9137
+ to 200ms but the row TEXT alongside still
9138
+ ran 150ms — same panel-row scope, two
9139
+ different rates. When a user hovered/pinned
9140
+ a row the rect background brightened in
9141
+ 200ms while the text fill + letter-spacing
9142
+ finished in 150ms. R474 closes that internal
9143
+ desync by lifting the text transitions to
9144
+ match. Whole recent-row state-flip now
9145
+ eases at 200ms ease-out across rect AND
9146
+ text. data-recent-row-text-transition='200ms'
9147
+ attr exposed for tests. R434 3-tier letter-
9148
+ spacing values unchanged; R363 fw + R55 fill
9149
+ brighten unchanged — only the timing axis
9150
+ shifts. */
9151
+ data-recent-row-text-transition="200ms"
8991
9152
  style={{
8992
- transition: 'fill 150ms ease-out, letter-spacing 150ms ease-out',
9153
+ transition: 'fill 200ms ease-out, letter-spacing 200ms ease-out',
8993
9154
  letterSpacing: isRowPinned ? '0.5px' :
8994
9155
  isRowHovered ? '0.25px' : '0px',
8995
9156
  }}
@@ -9645,6 +9806,20 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
9645
9806
  now ~1.25px below hitbox center (vs ~2.25px pre).
9646
9807
  No height change, no test ripple (other than this
9647
9808
  one), no R260/R268/R270 chrome regressions. */}
9809
+ {/* Round 473 / Loop — final cadence-sync follow-on,
9810
+ closing the legacy 150ms transition at the
9811
+ LEGEND-ROW tint scope. R459 (group-label hitbox)
9812
+ + R472 (recent-row hitbox) already lifted the
9813
+ two sibling panel-row hitboxes to 200ms; the
9814
+ legend-row was the last per-row tint still
9815
+ snapping at 150ms.
9816
+ After R473 the 200ms ease-out vocabulary is
9817
+ uniform across ALL three panel-row scopes —
9818
+ group-label, recent-signal, and legend — so
9819
+ hover/pin state-change cascades read coherently
9820
+ at every panel-tier surface. data-legend-row-
9821
+ tint-transition='200ms' attr exposed for tests.
9822
+ Geometry/paint unchanged. */}
9648
9823
  <rect
9649
9824
  x="6" y={row.y0 - 11}
9650
9825
  width="170" height="22" rx="3"
@@ -9653,7 +9828,8 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
9653
9828
  : hoveredStatus === row.key ? (isLight ? 0.08 : 0.12)
9654
9829
  : 1}
9655
9830
  data-legend-row-tinted={isPinned ? 'pinned' : hoveredStatus === row.key ? 'hover' : 'none'}
9656
- style={{ transition: 'fill 150ms ease-out, opacity 150ms ease-out' }}
9831
+ data-legend-row-tint-transition="200ms"
9832
+ style={{ transition: 'fill 200ms ease-out, opacity 200ms ease-out' }}
9657
9833
  />
9658
9834
  {/* Round 197 / Loop: swatch dot scales r 5.5 → 7 when its
9659
9835
  row is hovered or pinned. Pre-R197 the swatch was a
@@ -9738,6 +9914,30 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
9738
9914
  + pointerEvents:none all preserved. data-legend-
9739
9915
  pin-ring-stroke-width attr exposes the value for
9740
9916
  tests. */}
9917
+ {/* Round 477 / Loop — legend pin-ring gains a filter:
9918
+ drop-shadow glow on isPinned. Extends R476's
9919
+ drop-shadow idiom from hub-digit (focal scope)
9920
+ to the legend-row pin-ring (sibling pin-state
9921
+ surface). When a status row is pinned, the
9922
+ concentric ring around the swatch now lights
9923
+ up with a colour-matched halo using row.fill,
9924
+ reinforcing "this filter is locked" via a
9925
+ glow layer above the R402 sw bump + R181
9926
+ opacity fade-in.
9927
+ Hue: row.fill at 0.55 alpha — picks up each
9928
+ status tier's signature colour (working green /
9929
+ idle teal / offline slate). 3px blur stays
9930
+ subtle but unmistakable when the row is locked.
9931
+ Reduced-motion users skip the filter via R29
9932
+ a11y blanket (transition-duration → 0.001ms
9933
+ so the glow appears/disappears instantly with
9934
+ pin toggle).
9935
+ Filter is paint-only — bbox unchanged, R51
9936
+ overlap-test gated to g[data-node] descendants
9937
+ so this legend-internal ring is invisible to
9938
+ the probe anyway. Transition list extends to
9939
+ include 'filter 200ms ease-out' so the glow
9940
+ eases under the same cadence as opacity. */}
9741
9941
  <circle
9742
9942
  cx="16" cy={row.y0} r="8"
9743
9943
  fill="none"
@@ -9747,9 +9947,13 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
9747
9947
  data-legend-pin-ring={row.key}
9748
9948
  data-legend-pin-ring-pinned={isPinned ? 'true' : 'false'}
9749
9949
  data-legend-pin-ring-stroke-width="1.75"
9950
+ data-legend-pin-ring-glow={isPinned ? 'true' : 'false'}
9750
9951
  style={{
9751
9952
  pointerEvents: 'none',
9752
- transition: 'opacity 150ms ease-out',
9953
+ filter: isPinned
9954
+ ? `drop-shadow(0 0 3px ${row.fill}88)`
9955
+ : undefined,
9956
+ transition: 'opacity 150ms ease-out, filter 200ms ease-out',
9753
9957
  }}
9754
9958
  />
9755
9959
  {/* Round 219 / Loop: legend row text gains the same
@@ -9824,8 +10028,24 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
9824
10028
  group-label R432, legend-row R433). R55 fill
9825
10029
  150ms + R219 letter-spacing 150ms transition
9826
10030
  untouched — additive conditional case. */
10031
+ /* Round 475 / Loop — final closure of the panel-row
10032
+ text scope cadence-sync. R473 lifted the legend-
10033
+ row TINT RECT to 200ms; R474 lifted the recent-
10034
+ row TEXT to 200ms; R475 closes the matching
10035
+ legend-row text desync — fill + letter-spacing
10036
+ both 150 → 200ms ease-out. After R475 the 3-tier
10037
+ panel-row cadence family is fully 200ms across
10038
+ BOTH rect and text at every panel-row scope
10039
+ (group-label / recent-row / legend-row). Hover/
10040
+ pin state-flip at any panel-row tier reads as
10041
+ one motion-coherent unit. data-legend-row-
10042
+ label-transition='200ms' attr exposed for tests.
10043
+ R433 3-tier letter-spacing values (0/0.25/0.5)
10044
+ unchanged; R55 fill brighten unchanged — only
10045
+ the timing axis shifts. */
10046
+ data-legend-row-label-transition="200ms"
9827
10047
  style={{
9828
- transition: 'fill 150ms ease-out, letter-spacing 150ms ease-out',
10048
+ transition: 'fill 200ms ease-out, letter-spacing 200ms ease-out',
9829
10049
  letterSpacing: isPinned ? '0.5px' :
9830
10050
  hoveredStatus === row.key ? '0.25px' : '0px',
9831
10051
  }}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sleep2agi/agent-network-dashboard",
3
- "version": "0.5.2-preview.0",
3
+ "version": "0.5.2-preview.10",
4
4
  "description": "Agent Network Dashboard — Web UI for managing AI Agent networks",
5
5
  "scripts": {
6
6
  "dev": "next dev",
@@ -0,0 +1,92 @@
1
+ /* Round 470 verification: group <g> wrapper opacity transition
2
+ * cadence sync 150ms → 200ms. The R8 out-of-focus dim was the
3
+ * last cluster surface still at Tailwind's `transition-opacity`
4
+ * default cadence (150ms); R470 lifts it to 200ms ease-out to
5
+ * match the rest of the Hero D #147 200ms motion vocabulary.
6
+ *
7
+ * Contract:
8
+ * - every <g data-group> carries className containing
9
+ * 'transition-opacity duration-200 ease-out'
10
+ * - data-group-fade-transition='200ms' attr exposed
11
+ * - computed transition-duration is '0.2s' (200ms)
12
+ * - source-file utility classes wired
13
+ */
14
+ import { chromium } from 'playwright';
15
+ import { readFileSync } from 'node:fs';
16
+
17
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
18
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
19
+
20
+ const browser = await chromium.launch({ headless: true });
21
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
22
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
23
+ await ctx.addInitScript(() => {
24
+ try {
25
+ localStorage.setItem('anet-theme', 'cyber');
26
+ localStorage.setItem('anet-topo-layout', 'grid');
27
+ sessionStorage.setItem('anet_v3_auth', '1');
28
+ } catch {}
29
+ });
30
+ await ctx.route('**/api/hub/status*', async (route) => {
31
+ const r = await route.fetch();
32
+ const b = await r.json();
33
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
34
+ const mk = (alias, status) => ({
35
+ alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
36
+ network_id: nid, project_dir: null,
37
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
38
+ });
39
+ await route.fulfill({ response: r, json: { ...b, sessions: [
40
+ mk('alpha·1', 'working'),
41
+ mk('alpha·2', 'idle'),
42
+ mk('beta·1', 'working'),
43
+ mk('beta·2', 'idle'),
44
+ ] } });
45
+ });
46
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
47
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
48
+
49
+ const page = await ctx.newPage();
50
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
51
+ await page.waitForSelector('[data-group][data-group-fade-transition]', { timeout: 15000 });
52
+ await page.waitForTimeout(400);
53
+
54
+ const groups = await page.evaluate(() => {
55
+ const gs = [...document.querySelectorAll('[data-group]')];
56
+ return gs.map(g => {
57
+ const cs = getComputedStyle(g);
58
+ return {
59
+ key: g.getAttribute('data-group'),
60
+ className: g.getAttribute('class') || '',
61
+ fadeAttr: g.getAttribute('data-group-fade-transition'),
62
+ duration: cs.transitionDuration,
63
+ timing: cs.transitionTimingFunction,
64
+ property: cs.transitionProperty,
65
+ };
66
+ });
67
+ });
68
+
69
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
70
+ const sourceCN = /className="transition-opacity duration-200 ease-out anet-fade-in"/.test(src);
71
+ const sourceAttr = /data-group-fade-transition="200ms"/.test(src);
72
+
73
+ await browser.close();
74
+
75
+ const allCN200 = groups.every(g => /transition-opacity duration-200 ease-out/.test(g.className));
76
+ const allAttr200 = groups.every(g => g.fadeAttr === '200ms');
77
+ const allDur200 = groups.every(g => g.duration === '0.2s' || /(^|, )0\.2s/.test(g.duration));
78
+ const allEaseOut = groups.every(g => /ease(-out)?|cubic-bezier\(0,\s*0,\s*0\.2,\s*1\)/.test(g.timing) || g.timing.includes('cubic-bezier'));
79
+
80
+ const results = {
81
+ groups_count_ge_2: groups.length >= 2,
82
+ all_classname_200: allCN200,
83
+ all_attr_200: allAttr200,
84
+ all_computed_200ms: allDur200,
85
+ all_easing_ease_out: allEaseOut,
86
+ source_classname: sourceCN,
87
+ source_attr: sourceAttr,
88
+ };
89
+ const ok = Object.values(results).every(Boolean);
90
+ console.log(`${ok ? '✅' : '❌'} group <g> fade cadence 150→200:`, JSON.stringify(results),
91
+ '\n sample:', JSON.stringify(groups[0]));
92
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,104 @@
1
+ /* Round 479 verification: group-label parent text gains filter:
2
+ * drop-shadow glow on isPinned. 4th anchor in the R476/R477/R478
3
+ * drop-shadow visual-polish family.
4
+ *
5
+ * Contract:
6
+ * - at rest (no group pinned): every group label has data-group-
7
+ * label-glow='false' AND computed filter === 'none'
8
+ * - click a group-label hitbox: that group's label flips to
9
+ * glow='true' + filter starts with 'drop-shadow' using
10
+ * pal.legendAccent at 0x80 alpha
11
+ * - sibling group labels stay rest (no spillover)
12
+ * - source-file conditional wired
13
+ */
14
+ import { chromium } from 'playwright';
15
+ import { readFileSync } from 'node:fs';
16
+
17
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
18
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
19
+
20
+ const browser = await chromium.launch({ headless: true });
21
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
22
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
23
+ await ctx.addInitScript(() => {
24
+ try {
25
+ localStorage.setItem('anet-theme', 'cyber');
26
+ localStorage.setItem('anet-topo-layout', 'grid');
27
+ sessionStorage.setItem('anet_v3_auth', '1');
28
+ } catch {}
29
+ });
30
+ await ctx.route('**/api/hub/status*', async (route) => {
31
+ const r = await route.fetch();
32
+ const b = await r.json();
33
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
34
+ const mk = (alias, status) => ({
35
+ alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
36
+ network_id: nid, project_dir: null,
37
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
38
+ });
39
+ await route.fulfill({ response: r, json: { ...b, sessions: [
40
+ mk('alpha·1', 'working'), mk('alpha·2', 'idle'),
41
+ mk('beta·1', 'working'), mk('beta·2', 'idle'),
42
+ ] } });
43
+ });
44
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
45
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
46
+
47
+ const page = await ctx.newPage();
48
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
49
+ await page.waitForSelector('[data-group-label-glow]', { timeout: 15000 });
50
+ await page.waitForTimeout(500);
51
+
52
+ const readAll = () => page.evaluate(() => {
53
+ const labels = [...document.querySelectorAll('[data-group-label-glow]')];
54
+ return labels.map(l => {
55
+ const cs = getComputedStyle(l);
56
+ return {
57
+ key: l.getAttribute('data-group-label'),
58
+ glow: l.getAttribute('data-group-label-glow'),
59
+ filter: cs.filter,
60
+ };
61
+ });
62
+ });
63
+
64
+ const rest = await readAll();
65
+ const firstKey = rest[0]?.key;
66
+
67
+ // Click hitbox to pin the first group
68
+ let pinned = null;
69
+ if (firstKey) {
70
+ const hit = await page.$(`[data-group-label-hit="${firstKey}"]`);
71
+ if (hit) {
72
+ await hit.click();
73
+ await page.waitForTimeout(400);
74
+ pinned = await readAll();
75
+ }
76
+ }
77
+
78
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
79
+ const sourceGlowAttr = /data-group-label-glow=\{isPinned/.test(src);
80
+ const sourceDropShadow = /drop-shadow\(0 0 3px \$\{pal\.legendAccent\}80\)/.test(src);
81
+
82
+ await browser.close();
83
+
84
+ const restCount = rest.length;
85
+ const restAllFalse = rest.every(r => r.glow === 'false' && r.filter === 'none');
86
+ const pinnedTarget = pinned?.find(r => r.key === firstKey);
87
+ const pinTargetGlow = pinnedTarget?.glow === 'true';
88
+ const pinTargetHasShadow = pinnedTarget && /drop-shadow/.test(pinnedTarget.filter);
89
+ const pinSiblingsStill = pinned ? pinned.filter(r => r.key !== firstKey).every(r => r.glow === 'false') : false;
90
+
91
+ const results = {
92
+ rest_count_ge_2: restCount >= 2,
93
+ rest_all_false: restAllFalse,
94
+ pinned_target_glow: pinTargetGlow,
95
+ pinned_target_shadow: pinTargetHasShadow,
96
+ pinned_siblings_rest: pinSiblingsStill,
97
+ source_glow_attr: sourceGlowAttr,
98
+ source_drop_shadow: sourceDropShadow,
99
+ };
100
+ const ok = Object.values(results).every(Boolean);
101
+ console.log(`${ok ? '✅' : '❌'} group-label drop-shadow glow:`, JSON.stringify(results),
102
+ '\n rest:', JSON.stringify(rest),
103
+ '\n pinned target:', JSON.stringify(pinnedTarget));
104
+ process.exit(ok ? 0 : 1);