@sleep2agi/agent-network-dashboard 0.4.4-preview.0 → 0.4.5-preview.1

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 (282) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +6 -6
  3. package/.next/diagnostics/route-bundle-stats.json +106 -92
  4. package/.next/fallback-build-manifest.json +3 -3
  5. package/.next/prerender-manifest.json +3 -3
  6. package/.next/server/app/_global-error/page/build-manifest.json +3 -3
  7. package/.next/server/app/_global-error.html +1 -1
  8. package/.next/server/app/_global-error.rsc +1 -1
  9. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  12. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  13. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  14. package/.next/server/app/_not-found/page/build-manifest.json +3 -3
  15. package/.next/server/app/_not-found/page.js.nft.json +1 -1
  16. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  17. package/.next/server/app/_not-found.html +2 -2
  18. package/.next/server/app/_not-found.rsc +12 -12
  19. package/.next/server/app/_not-found.segments/_full.segment.rsc +12 -12
  20. package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  21. package/.next/server/app/_not-found.segments/_index.segment.rsc +7 -7
  22. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  23. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  24. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  25. package/.next/server/app/admin/page/build-manifest.json +3 -3
  26. package/.next/server/app/admin/page.js.nft.json +1 -1
  27. package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
  28. package/.next/server/app/admin.html +2 -2
  29. package/.next/server/app/admin.rsc +14 -14
  30. package/.next/server/app/admin.segments/_full.segment.rsc +14 -14
  31. package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
  32. package/.next/server/app/admin.segments/_index.segment.rsc +7 -7
  33. package/.next/server/app/admin.segments/_tree.segment.rsc +2 -2
  34. package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +4 -4
  35. package/.next/server/app/admin.segments/admin.segment.rsc +3 -3
  36. package/.next/server/app/index.html +2 -2
  37. package/.next/server/app/index.rsc +14 -14
  38. package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
  39. package/.next/server/app/index.segments/_full.segment.rsc +14 -14
  40. package/.next/server/app/index.segments/_head.segment.rsc +4 -4
  41. package/.next/server/app/index.segments/_index.segment.rsc +7 -7
  42. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  43. package/.next/server/app/login/page/build-manifest.json +3 -3
  44. package/.next/server/app/login/page.js.nft.json +1 -1
  45. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  46. package/.next/server/app/login.html +2 -2
  47. package/.next/server/app/login.rsc +14 -14
  48. package/.next/server/app/login.segments/_full.segment.rsc +14 -14
  49. package/.next/server/app/login.segments/_head.segment.rsc +4 -4
  50. package/.next/server/app/login.segments/_index.segment.rsc +7 -7
  51. package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
  52. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
  53. package/.next/server/app/login.segments/login.segment.rsc +3 -3
  54. package/.next/server/app/logs/page/build-manifest.json +3 -3
  55. package/.next/server/app/logs/page.js.nft.json +1 -1
  56. package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
  57. package/.next/server/app/logs.html +2 -2
  58. package/.next/server/app/logs.rsc +14 -14
  59. package/.next/server/app/logs.segments/_full.segment.rsc +14 -14
  60. package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
  61. package/.next/server/app/logs.segments/_index.segment.rsc +7 -7
  62. package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
  63. package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
  64. package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
  65. package/.next/server/app/messages/page/build-manifest.json +3 -3
  66. package/.next/server/app/messages/page.js.nft.json +1 -1
  67. package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
  68. package/.next/server/app/messages.html +2 -2
  69. package/.next/server/app/messages.rsc +14 -14
  70. package/.next/server/app/messages.segments/_full.segment.rsc +14 -14
  71. package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
  72. package/.next/server/app/messages.segments/_index.segment.rsc +7 -7
  73. package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
  74. package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
  75. package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
  76. package/.next/server/app/node/page/build-manifest.json +3 -3
  77. package/.next/server/app/node/page.js.nft.json +1 -1
  78. package/.next/server/app/node/page_client-reference-manifest.js +1 -1
  79. package/.next/server/app/node.html +2 -2
  80. package/.next/server/app/node.rsc +14 -14
  81. package/.next/server/app/node.segments/_full.segment.rsc +14 -14
  82. package/.next/server/app/node.segments/_head.segment.rsc +4 -4
  83. package/.next/server/app/node.segments/_index.segment.rsc +7 -7
  84. package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
  85. package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
  86. package/.next/server/app/node.segments/node.segment.rsc +3 -3
  87. package/.next/server/app/nodes/page/build-manifest.json +3 -3
  88. package/.next/server/app/nodes/page.js.nft.json +1 -1
  89. package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
  90. package/.next/server/app/nodes.html +2 -2
  91. package/.next/server/app/nodes.rsc +14 -14
  92. package/.next/server/app/nodes.segments/_full.segment.rsc +14 -14
  93. package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
  94. package/.next/server/app/nodes.segments/_index.segment.rsc +7 -7
  95. package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
  96. package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
  97. package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
  98. package/.next/server/app/page/build-manifest.json +3 -3
  99. package/.next/server/app/page.js.nft.json +1 -1
  100. package/.next/server/app/page_client-reference-manifest.js +1 -1
  101. package/.next/server/app/server-logs/page/build-manifest.json +3 -3
  102. package/.next/server/app/server-logs/page.js.nft.json +1 -1
  103. package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
  104. package/.next/server/app/server-logs.html +2 -2
  105. package/.next/server/app/server-logs.rsc +14 -14
  106. package/.next/server/app/server-logs.segments/_full.segment.rsc +14 -14
  107. package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
  108. package/.next/server/app/server-logs.segments/_index.segment.rsc +7 -7
  109. package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
  110. package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
  111. package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
  112. package/.next/server/app/settings/networks/page/build-manifest.json +3 -3
  113. package/.next/server/app/settings/networks/page.js.nft.json +1 -1
  114. package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
  115. package/.next/server/app/settings/networks.html +2 -2
  116. package/.next/server/app/settings/networks.rsc +14 -14
  117. package/.next/server/app/settings/networks.segments/_full.segment.rsc +14 -14
  118. package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
  119. package/.next/server/app/settings/networks.segments/_index.segment.rsc +7 -7
  120. package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
  121. package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
  122. package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
  123. package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
  124. package/.next/server/app/settings/page/build-manifest.json +3 -3
  125. package/.next/server/app/settings/page.js.nft.json +1 -1
  126. package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  127. package/.next/server/app/settings/tokens/page/build-manifest.json +3 -3
  128. package/.next/server/app/settings/tokens/page.js.nft.json +1 -1
  129. package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
  130. package/.next/server/app/settings/tokens.html +2 -2
  131. package/.next/server/app/settings/tokens.rsc +14 -14
  132. package/.next/server/app/settings/tokens.segments/_full.segment.rsc +14 -14
  133. package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
  134. package/.next/server/app/settings/tokens.segments/_index.segment.rsc +7 -7
  135. package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
  136. package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
  137. package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
  138. package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
  139. package/.next/server/app/settings.html +2 -2
  140. package/.next/server/app/settings.rsc +14 -14
  141. package/.next/server/app/settings.segments/_full.segment.rsc +14 -14
  142. package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
  143. package/.next/server/app/settings.segments/_index.segment.rsc +7 -7
  144. package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  145. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
  146. package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
  147. package/.next/server/app/tasks/[id]/page/build-manifest.json +3 -3
  148. package/.next/server/app/tasks/[id]/page.js.nft.json +1 -1
  149. package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
  150. package/.next/server/app/tasks/page/build-manifest.json +3 -3
  151. package/.next/server/app/tasks/page.js.nft.json +1 -1
  152. package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
  153. package/.next/server/app/tasks.html +2 -2
  154. package/.next/server/app/tasks.rsc +14 -14
  155. package/.next/server/app/tasks.segments/_full.segment.rsc +14 -14
  156. package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
  157. package/.next/server/app/tasks.segments/_index.segment.rsc +7 -7
  158. package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
  159. package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
  160. package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
  161. package/.next/server/chunks/ssr/[root-of-the-server]__0_jnflc._.js +3 -0
  162. package/.next/server/chunks/ssr/[root-of-the-server]__0_jnflc._.js.map +1 -0
  163. package/.next/server/chunks/ssr/[root-of-the-server]__0a0ffmv._.js +3 -0
  164. package/.next/server/chunks/ssr/[root-of-the-server]__0a0ffmv._.js.map +1 -0
  165. package/.next/server/chunks/ssr/[root-of-the-server]__12mpoig._.js +3 -0
  166. package/.next/server/chunks/ssr/[root-of-the-server]__12mpoig._.js.map +1 -0
  167. package/.next/server/chunks/ssr/_0d-fuox._.js +3 -0
  168. package/.next/server/chunks/ssr/_0d-fuox._.js.map +1 -0
  169. package/.next/server/chunks/ssr/app_0.0wpa~._.js +3 -0
  170. package/.next/server/chunks/ssr/app_0.0wpa~._.js.map +1 -0
  171. package/.next/server/chunks/ssr/app_0dk5wao._.js +1 -1
  172. package/.next/server/chunks/ssr/app_0dk5wao._.js.map +1 -1
  173. package/.next/server/chunks/ssr/app_0et5yua._.js +3 -0
  174. package/.next/server/chunks/ssr/app_0et5yua._.js.map +1 -0
  175. package/.next/server/chunks/ssr/app_0ff~kjj._.js +9 -0
  176. package/.next/server/chunks/ssr/app_0ff~kjj._.js.map +1 -0
  177. package/.next/server/chunks/ssr/app_0izi28j._.js +3 -0
  178. package/.next/server/chunks/ssr/app_0izi28j._.js.map +1 -0
  179. package/.next/server/chunks/ssr/app_components_08ys5y3._.js +9 -0
  180. package/.next/server/chunks/ssr/app_components_08ys5y3._.js.map +1 -0
  181. package/.next/server/chunks/ssr/app_login_page_tsx_0z71ad3._.js +1 -1
  182. package/.next/server/chunks/ssr/app_login_page_tsx_0z71ad3._.js.map +1 -1
  183. package/.next/server/chunks/ssr/app_page_tsx_0es_sk2._.js +1 -1
  184. package/.next/server/chunks/ssr/app_page_tsx_0es_sk2._.js.map +1 -1
  185. package/.next/server/chunks/ssr/app_tasks_page_tsx_0z-d6mp._.js +1 -1
  186. package/.next/server/chunks/ssr/app_tasks_page_tsx_0z-d6mp._.js.map +1 -1
  187. package/.next/server/chunks/ssr/node_modules_0ck2~9g._.js +3 -0
  188. package/.next/server/chunks/ssr/node_modules_0ck2~9g._.js.map +1 -0
  189. package/.next/server/middleware-build-manifest.js +6 -6
  190. package/.next/server/pages/404.html +2 -2
  191. package/.next/server/pages/500.html +1 -1
  192. package/.next/server/server-reference-manifest.js +1 -1
  193. package/.next/server/server-reference-manifest.json +1 -1
  194. package/.next/static/chunks/0257pdz1-imal.js +2 -0
  195. package/.next/static/chunks/02flrewxlf_m_.js +1 -0
  196. package/.next/static/chunks/051gslfjka1kj.js +1 -0
  197. package/.next/static/chunks/06t_vzei8s9_h.js +1 -0
  198. package/.next/static/chunks/0a1wbl-spby3_.css +2 -0
  199. package/.next/static/chunks/0e-.zgw81gela.js +31 -0
  200. package/.next/static/chunks/0e7c40p~wq665.js +1 -0
  201. package/.next/static/chunks/0g7m7zk.v7g6h.js +1 -0
  202. package/.next/static/chunks/0gs9-~h7-pkuu.js +7 -0
  203. package/.next/static/chunks/0j3s_1ee66awv.js +1 -0
  204. package/.next/static/chunks/0khpyxpdrp~ty.js +1 -0
  205. package/.next/static/chunks/0lo_g.q1fm-hr.js +7 -0
  206. package/.next/static/chunks/0w1zwt8kq_7nb.js +1 -0
  207. package/.next/static/chunks/1031v6nw33ud3.js +1 -0
  208. package/.next/static/chunks/13.dst7liziwo.js +1 -0
  209. package/.next/static/chunks/{turbopack-0vsvjs02l449s.js → turbopack-01f1xs~cq_~si.js} +1 -1
  210. package/.next/trace +2 -2
  211. package/.next/trace-build +1 -1
  212. package/app/components/AppShell.tsx +9 -1
  213. package/app/components/CommandPalette.tsx +316 -0
  214. package/app/components/EmptyState.tsx +189 -0
  215. package/app/components/HealthBanner.tsx +132 -0
  216. package/app/components/HelpOverlay.tsx +121 -0
  217. package/app/components/LoadingSkeleton.tsx +81 -40
  218. package/app/components/Sidebar.tsx +90 -11
  219. package/app/components/TopoGraph.tsx +151 -62
  220. package/app/components/UserBar.tsx +16 -2
  221. package/app/globals.css +175 -0
  222. package/app/login/page.tsx +26 -13
  223. package/app/logs/page.tsx +2 -5
  224. package/app/messages/page.tsx +2 -5
  225. package/app/nodes/page.tsx +6 -1
  226. package/app/page.tsx +109 -12
  227. package/app/settings/networks/page.tsx +6 -1
  228. package/app/settings/page.tsx +56 -29
  229. package/app/settings/tokens/page.tsx +6 -1
  230. package/app/tasks/page.tsx +39 -14
  231. package/docs/loop-design-rounds.md +797 -0
  232. package/docs/oss-readiness-report.md +189 -0
  233. package/docs/screenshots/0.4.5-preview.1/cyber-admin.png +0 -0
  234. package/docs/screenshots/0.4.5-preview.1/cyber-cmdk-search.png +0 -0
  235. package/docs/screenshots/0.4.5-preview.1/cyber-cmdk.png +0 -0
  236. package/docs/screenshots/0.4.5-preview.1/cyber-login.png +0 -0
  237. package/docs/screenshots/0.4.5-preview.1/cyber-overview.png +0 -0
  238. package/docs/screenshots/0.4.5-preview.1/cyber-settings.png +0 -0
  239. package/docs/screenshots/0.4.5-preview.1/cyber-tasks.png +0 -0
  240. package/docs/screenshots/0.4.5-preview.1/light-admin.png +0 -0
  241. package/docs/screenshots/0.4.5-preview.1/light-cmdk-search.png +0 -0
  242. package/docs/screenshots/0.4.5-preview.1/light-cmdk.png +0 -0
  243. package/docs/screenshots/0.4.5-preview.1/light-login.png +0 -0
  244. package/docs/screenshots/0.4.5-preview.1/light-overview.png +0 -0
  245. package/docs/screenshots/0.4.5-preview.1/light-settings.png +0 -0
  246. package/docs/screenshots/0.4.5-preview.1/light-tasks.png +0 -0
  247. package/package.json +1 -1
  248. package/.next/server/chunks/ssr/[root-of-the-server]__093ymui._.js +0 -3
  249. package/.next/server/chunks/ssr/[root-of-the-server]__093ymui._.js.map +0 -1
  250. package/.next/server/chunks/ssr/[root-of-the-server]__0i1gizm._.js +0 -9
  251. package/.next/server/chunks/ssr/[root-of-the-server]__0i1gizm._.js.map +0 -1
  252. package/.next/server/chunks/ssr/[root-of-the-server]__0~yad5w._.js +0 -3
  253. package/.next/server/chunks/ssr/[root-of-the-server]__0~yad5w._.js.map +0 -1
  254. package/.next/server/chunks/ssr/_06_xh1~._.js +0 -3
  255. package/.next/server/chunks/ssr/_06_xh1~._.js.map +0 -1
  256. package/.next/server/chunks/ssr/app_01vhyfx._.js +0 -9
  257. package/.next/server/chunks/ssr/app_01vhyfx._.js.map +0 -1
  258. package/.next/server/chunks/ssr/app_023a94i._.js +0 -3
  259. package/.next/server/chunks/ssr/app_023a94i._.js.map +0 -1
  260. package/.next/server/chunks/ssr/app_0basiqb._.js +0 -3
  261. package/.next/server/chunks/ssr/app_0basiqb._.js.map +0 -1
  262. package/.next/server/chunks/ssr/app_0pljk5o._.js +0 -3
  263. package/.next/server/chunks/ssr/app_0pljk5o._.js.map +0 -1
  264. package/.next/server/chunks/ssr/node_modules_next_dist_0t-hj0x._.js +0 -3
  265. package/.next/server/chunks/ssr/node_modules_next_dist_0t-hj0x._.js.map +0 -1
  266. package/.next/static/chunks/0.z~9klrgd.t-.js +0 -1
  267. package/.next/static/chunks/08aj9~75raaah.js +0 -7
  268. package/.next/static/chunks/08iom_z8w-wmz.js +0 -7
  269. package/.next/static/chunks/08v0-ki6vwpxf.js +0 -1
  270. package/.next/static/chunks/0dkkv90do9a4r.js +0 -1
  271. package/.next/static/chunks/0e-wrhqh4h3r5.js +0 -2
  272. package/.next/static/chunks/0ejz4zwhu3a3y.js +0 -1
  273. package/.next/static/chunks/0o5zj~5vjeks3.js +0 -1
  274. package/.next/static/chunks/0uqfv6rc-xdru.js +0 -1
  275. package/.next/static/chunks/0u~_.32f.midj.css +0 -2
  276. package/.next/static/chunks/0xrnwfefpke-g.js +0 -31
  277. package/.next/static/chunks/0y_po~f~fk~ap.js +0 -1
  278. package/.next/static/chunks/14sm.ta0zylsj.js +0 -1
  279. package/.next/static/chunks/18ac93ii3mjlj.js +0 -1
  280. /package/.next/static/{jTh5m77FIfQccc0gftvik → wcTnZXyx_R19_Ur1_cGMP}/_buildManifest.js +0 -0
  281. /package/.next/static/{jTh5m77FIfQccc0gftvik → wcTnZXyx_R19_Ur1_cGMP}/_clientMiddlewareManifest.js +0 -0
  282. /package/.next/static/{jTh5m77FIfQccc0gftvik → wcTnZXyx_R19_Ur1_cGMP}/_ssgManifest.js +0 -0
@@ -0,0 +1,121 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useState } from 'react';
4
+
5
+ /** Help overlay — press `?` to see keyboard shortcuts.
6
+ * Restrained: no animation noise, single column, all global keys in one
7
+ * place. Closes on Esc or `?` again or click-outside.
8
+ */
9
+
10
+ interface Shortcut {
11
+ keys: string[];
12
+ label: string;
13
+ }
14
+
15
+ const SHORTCUTS: { group: string; items: Shortcut[] }[] = [
16
+ {
17
+ group: 'Global',
18
+ items: [
19
+ { keys: ['⌘', 'K'], label: 'Open command palette (also Ctrl+K)' },
20
+ { keys: ['/'], label: 'Open command palette' },
21
+ { keys: ['?'], label: 'Toggle this help overlay' },
22
+ { keys: ['Esc'], label: 'Close any open modal' },
23
+ ],
24
+ },
25
+ {
26
+ group: 'Command palette',
27
+ items: [
28
+ { keys: ['↑', '↓'], label: 'Move selection' },
29
+ { keys: ['↵'], label: 'Activate selected command' },
30
+ { keys: ['type'], label: 'Filter — searches commands, agents, tasks' },
31
+ ],
32
+ },
33
+ {
34
+ group: 'Pages',
35
+ items: [
36
+ { keys: ['click', 'sidebar logo'], label: 'Back to Overview' },
37
+ { keys: ['Cmd+K', 'Go to agent X'], label: 'Jump straight to a node' },
38
+ ],
39
+ },
40
+ ];
41
+
42
+ export function HelpOverlay() {
43
+ const [open, setOpen] = useState(false);
44
+
45
+ useEffect(() => {
46
+ const onKey = (e: KeyboardEvent) => {
47
+ // `?` toggles — Shift+/ on US layout. Only when no input is focused.
48
+ if (e.key === '?' && document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') {
49
+ e.preventDefault();
50
+ setOpen(prev => !prev);
51
+ return;
52
+ }
53
+ if (e.key === 'Escape' && open) {
54
+ setOpen(false);
55
+ }
56
+ };
57
+ window.addEventListener('keydown', onKey);
58
+ return () => window.removeEventListener('keydown', onKey);
59
+ }, [open]);
60
+
61
+ if (!open) return null;
62
+
63
+ return (
64
+ <div
65
+ role="dialog"
66
+ aria-modal="true"
67
+ aria-label="Keyboard shortcuts"
68
+ className="anet-help fixed inset-0 z-[101] flex items-center justify-center px-4"
69
+ onClick={() => setOpen(false)}
70
+ >
71
+ <div className="absolute inset-0 bg-black/50 backdrop-blur-sm" aria-hidden />
72
+ <div
73
+ className="relative w-full max-w-md rounded-xl border border-[#2a2a4a] bg-[#0d0d1a] shadow-2xl shadow-black/40 overflow-hidden"
74
+ onClick={e => e.stopPropagation()}
75
+ >
76
+ <div className="flex items-center justify-between border-b border-[#2a2a4a] px-4 py-2.5">
77
+ <div className="flex items-center gap-2 text-sm font-medium text-gray-200">
78
+ <svg width="14" height="14" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="1.5" className="text-gray-500">
79
+ <path strokeLinecap="round" strokeLinejoin="round" d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
80
+ </svg>
81
+ Keyboard shortcuts
82
+ </div>
83
+ <button
84
+ onClick={() => setOpen(false)}
85
+ aria-label="Close help"
86
+ className="text-gray-500 hover:text-gray-300 text-lg leading-none px-1"
87
+ >
88
+ ×
89
+ </button>
90
+ </div>
91
+
92
+ <div className="px-4 py-3 max-h-[70vh] overflow-y-auto">
93
+ {SHORTCUTS.map(g => (
94
+ <div key={g.group} className="mb-3 last:mb-0">
95
+ <div className="text-[10px] uppercase tracking-[0.12em] text-gray-600 mb-1.5">{g.group}</div>
96
+ <ul className="space-y-1.5">
97
+ {g.items.map((s, i) => (
98
+ <li key={i} className="flex items-center gap-3 text-xs">
99
+ <span className="flex items-center gap-1">
100
+ {s.keys.map((k, ki) => (
101
+ <kbd key={ki} className="text-[10px] font-mono border border-[#2a2a4a] rounded px-1.5 py-0.5 text-gray-400 bg-[#1a1a2a]/40">
102
+ {k}
103
+ </kbd>
104
+ ))}
105
+ </span>
106
+ <span className="text-gray-400 flex-1">{s.label}</span>
107
+ </li>
108
+ ))}
109
+ </ul>
110
+ </div>
111
+ ))}
112
+ </div>
113
+
114
+ <div className="border-t border-[#2a2a4a] px-4 py-1.5 text-[10px] text-gray-600 flex items-center justify-between">
115
+ <span>Press <kbd className="font-mono">?</kbd> to toggle</span>
116
+ <span>or <kbd className="font-mono">esc</kbd> to close</span>
117
+ </div>
118
+ </div>
119
+ </div>
120
+ );
121
+ }
@@ -1,42 +1,86 @@
1
1
  'use client';
2
2
 
3
+ /**
4
+ * Loading skeleton — mirrors the actual Overview layout structure so the
5
+ * page doesn't appear to "shift" once data arrives. Uses the same
6
+ * `anet-skeleton-pulse` rhythm as the brand pulse (1.6s opacity drift,
7
+ * no scale, no blur, no glow) so the loading animation feels native to
8
+ * the rest of the dashboard rather than a generic spinner.
9
+ *
10
+ * Bars are theme-aware: anet-skeleton-bar (existing CSS shim resolves this to
11
+ * var(--bg-elevated) on light/mint), so light theme shows soft grey blocks
12
+ * on white cards and dark theme shows lighter blocks on navy cards.
13
+ */
3
14
  export function LoadingSkeleton() {
4
15
  return (
5
- <div className="min-h-screen bg-[#0a0a1a] text-gray-100 p-6 font-mono animate-pulse">
6
- {/* Header skeleton */}
7
- <div className="flex items-center justify-between mb-8">
8
- <div>
9
- <div className="h-7 w-48 bg-gray-800 rounded mb-2" />
10
- <div className="h-4 w-64 bg-gray-800/60 rounded" />
11
- </div>
12
- <div className="flex gap-3">
13
- {[1, 2, 3].map(i => (
14
- <div key={i} className="w-[72px] h-[60px] bg-gray-800/40 rounded-lg border border-gray-800" />
15
- ))}
16
+ <div className="min-h-screen bg-[#0a0a1a] text-gray-100 p-4 sm:p-6 font-mono">
17
+ {/* KPI top strip — 4 cards matching StatsBar */}
18
+ <div className="grid grid-cols-2 sm:grid-cols-4 gap-3 mb-8 anet-skeleton-pulse">
19
+ {[1, 2, 3, 4].map(i => (
20
+ <div key={i} className="rounded-xl border border-[#2a2a4a] bg-[#111128] px-4 py-3">
21
+ <Bar w="2.5rem" h="1.75rem" />
22
+ <Bar w="3.5rem" h="0.75rem" className="mt-2" />
23
+ <Bar w="5rem" h="0.625rem" className="mt-1" />
24
+ </div>
25
+ ))}
26
+ </div>
27
+
28
+ {/* Dispatch + UserBar row */}
29
+ <div className="flex items-center gap-3 mb-3 anet-skeleton-pulse">
30
+ <Bar w="6rem" h="2.5rem" rounded="0.75rem" />
31
+ <div className="flex-1 rounded-lg border border-[#2a2a4a] bg-[#111128] px-4 py-2.5 flex items-center gap-3">
32
+ <div className="w-8 h-8 rounded-full anet-skeleton-bar" />
33
+ <div className="flex-1">
34
+ <Bar w="6rem" h="0.875rem" />
35
+ <Bar w="10rem" h="0.625rem" className="mt-1" />
36
+ </div>
16
37
  </div>
17
38
  </div>
18
39
 
19
- {/* Broadcast bar skeleton */}
20
- <div className="mb-6 flex gap-2">
21
- <div className="flex-1 h-10 bg-gray-800/40 rounded-lg" />
22
- <div className="w-28 h-10 bg-gray-800/40 rounded-lg" />
40
+ {/* Config bar */}
41
+ <div className="mb-6 rounded-lg border border-[#2a2a4a] bg-[#111128] px-4 py-3 anet-skeleton-pulse">
42
+ <Bar w="14rem" h="0.875rem" />
43
+ </div>
44
+
45
+ {/* Stat strip 3 cards */}
46
+ <div className="grid grid-cols-3 gap-2 sm:gap-3 mb-3 anet-skeleton-pulse">
47
+ {[1, 2, 3].map(i => (
48
+ <div key={i} className="rounded-xl border border-[#2a2a4a] bg-[#111128] px-3 py-3">
49
+ <Bar w="2rem" h="1.25rem" />
50
+ <Bar w="2.5rem" h="0.75rem" className="mt-1" />
51
+ <Bar w="3.5rem" h="0.625rem" className="mt-px" />
52
+ </div>
53
+ ))}
23
54
  </div>
24
55
 
25
- {/* Topology skeleton */}
26
- <div className="w-full max-w-4xl mx-auto mb-8 h-64 bg-gray-800/20 rounded-xl" />
56
+ {/* Nav rail 3 cards */}
57
+ <div className="grid grid-cols-3 gap-2 sm:gap-3 mb-6 anet-skeleton-pulse">
58
+ {[1, 2, 3].map(i => (
59
+ <div key={i} className="rounded-xl border border-[#2a2a4a] bg-[#111128] px-3 py-2.5 flex items-center justify-center gap-2">
60
+ <div className="w-4 h-4 rounded anet-skeleton-bar" />
61
+ <Bar w="4rem" h="0.75rem" />
62
+ </div>
63
+ ))}
64
+ </div>
27
65
 
28
- {/* Card grid skeleton */}
29
- <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
66
+ {/* Broadcast bar */}
67
+ <div className="mb-6 flex gap-2 anet-skeleton-pulse">
68
+ <div className="flex-1 h-10 rounded-lg border border-[#2a2a4a] bg-[#111128]" />
69
+ <div className="w-28 h-10 rounded-lg anet-skeleton-bar" />
70
+ </div>
71
+
72
+ {/* Agent card grid */}
73
+ <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 gap-3 sm:gap-4 anet-skeleton-pulse">
30
74
  {[1, 2, 3, 4].map(i => (
31
- <div key={i} className="rounded-xl border border-gray-800/40 p-4 bg-gray-800/20">
75
+ <div key={i} className="rounded-xl border border-[#2a2a4a] bg-[#111128] p-4">
32
76
  <div className="flex items-center gap-2 mb-3">
33
- <div className="w-2.5 h-2.5 rounded-full bg-gray-700" />
34
- <div className="h-4 w-24 bg-gray-700 rounded" />
77
+ <div className="w-2 h-2 rounded-full anet-skeleton-bar" />
78
+ <Bar w="6rem" h="0.875rem" />
35
79
  </div>
36
80
  <div className="space-y-2">
37
- <div className="h-3 w-full bg-gray-800/60 rounded" />
38
- <div className="h-3 w-3/4 bg-gray-800/60 rounded" />
39
- <div className="h-3 w-1/2 bg-gray-800/60 rounded" />
81
+ <Bar w="100%" h="0.625rem" />
82
+ <Bar w="75%" h="0.625rem" />
83
+ <Bar w="55%" h="0.625rem" />
40
84
  </div>
41
85
  </div>
42
86
  ))}
@@ -45,21 +89,18 @@ export function LoadingSkeleton() {
45
89
  );
46
90
  }
47
91
 
48
- export function EmptyState({ hint }: { hint?: { global_count?: number; filtered_network?: string } }) {
92
+ /** Single shimmer bar uses `anet-skeleton-bar` so theme-specific bar
93
+ * color (dark navy on dark themes, mid-grey on light) overrides the
94
+ * default. */
95
+ function Bar({ w, h, rounded, className }: { w: string; h: string; rounded?: string; className?: string }) {
49
96
  return (
50
- <div className="text-center py-16">
51
- <div className="text-gray-600 text-4xl mb-4">--</div>
52
- <h3 className="text-gray-400 text-lg font-medium mb-2">No agents in this network</h3>
53
- {hint?.global_count ? (
54
- <p className="text-gray-500 text-sm max-w-md mx-auto">
55
- Server has <span className="text-cyan-400 font-medium">{hint.global_count}</span> nodes globally,
56
- but none are registered to the current network. Switch network or contact admin.
57
- </p>
58
- ) : (
59
- <p className="text-gray-600 text-sm max-w-md mx-auto">
60
- Agent sessions will appear here once they connect to the CommHub.
61
- </p>
62
- )}
63
- </div>
97
+ <div
98
+ className={`anet-skeleton-bar ${className || ''}`}
99
+ style={{ width: w, height: h, borderRadius: rounded || '0.375rem' }}
100
+ />
64
101
  );
65
102
  }
103
+
104
+ // EmptyState export removed in 0.4.5 — replaced by EmptyState.tsx with
105
+ // per-variant glyphs and a NodesEmptyState wrapper for the Overview
106
+ // hint-aware behavior. See app/components/EmptyState.tsx.
@@ -101,17 +101,11 @@ export function Sidebar() {
101
101
  ${mobileOpen ? 'translate-x-0' : '-translate-x-full'}
102
102
  lg:translate-x-0 lg:static lg:shrink-0
103
103
  `}>
104
- {/* Logo */}
105
- <div className={`px-4 py-5 border-b border-[#2a2a4a] ${collapsed ? 'text-center' : ''}`}>
106
- {collapsed ? (
107
- <span className="text-cyan-400 font-bold text-lg">A</span>
108
- ) : (
109
- <div>
110
- <div className="text-white font-bold text-sm">Agent Network</div>
111
- <div className="text-gray-600 text-xs">Dashboard</div>
112
- </div>
113
- )}
114
- </div>
104
+ {/* Brand header — round 4: 3-node mesh mark matches /login,
105
+ with an inline live "online" pulse so every page surfaces
106
+ fleet health without leaving for /nodes. */}
107
+ <SidebarBrand collapsed={collapsed} />
108
+ <div className={`border-b border-[#2a2a4a]`} />
115
109
 
116
110
  {/* Network list */}
117
111
  {!collapsed && networks.length > 0 && (
@@ -157,6 +151,24 @@ export function Sidebar() {
157
151
  Sign out
158
152
  </button>
159
153
  )}
154
+ {!collapsed && (
155
+ <button
156
+ onClick={() => {
157
+ // Dispatch a synthetic keypress so the global Cmd+K listener fires
158
+ window.dispatchEvent(new KeyboardEvent('keydown', { key: 'k', metaKey: true, ctrlKey: true }));
159
+ }}
160
+ className="w-full flex items-center justify-between gap-2 px-5 py-2 text-[11px] text-gray-600 hover:text-gray-400 hover:bg-[#1a1a2a] transition-colors"
161
+ aria-label="Open command palette"
162
+ >
163
+ <span className="flex items-center gap-2">
164
+ <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
165
+ <path strokeLinecap="round" strokeLinejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
166
+ </svg>
167
+ Quick search
168
+ </span>
169
+ <kbd className="text-[10px] border border-current rounded px-1 py-0.5 opacity-60 font-mono">⌘K</kbd>
170
+ </button>
171
+ )}
160
172
  <div className={`flex items-center gap-2 px-3 py-2 ${collapsed ? 'justify-center' : 'justify-between'}`}>
161
173
  <ThemeSwitcher compact={collapsed} />
162
174
  <button
@@ -174,3 +186,70 @@ export function Sidebar() {
174
186
  </>
175
187
  );
176
188
  }
189
+
190
+ interface StatusData {
191
+ sessions?: Array<{ status?: string; alias?: string }>;
192
+ }
193
+
194
+ const statusFetcher = (url: string) =>
195
+ fetch(url).then(r => r.ok ? r.json() as Promise<StatusData> : { sessions: [] });
196
+
197
+ function SidebarBrand({ collapsed }: { collapsed: boolean }) {
198
+ // Live online count — re-uses the same /api/hub/status endpoint the
199
+ // Overview already polls, so SWR dedupes the request. Pulse is muted —
200
+ // emerald dot at 4px with a 1.6s slow opacity pulse, no glow, no halo.
201
+ const { data } = useSWR<StatusData>('/api/hub/status', statusFetcher, {
202
+ refreshInterval: 10000,
203
+ dedupingInterval: 4000,
204
+ });
205
+ const sessions = data?.sessions || [];
206
+ const online = sessions.filter(s => s.status && s.status !== 'offline').length;
207
+ const total = sessions.length;
208
+
209
+ if (collapsed) {
210
+ return (
211
+ <Link href="/" className="block px-3 py-4 flex justify-center hover:opacity-80 transition-opacity" aria-label="Agent Network — home">
212
+ <BrandMark size={32} />
213
+ </Link>
214
+ );
215
+ }
216
+
217
+ return (
218
+ <Link
219
+ href="/"
220
+ className="block px-4 py-4 flex items-center gap-3 hover:bg-[#11112a]/40 transition-colors rounded-r-xl"
221
+ aria-label="Agent Network — home"
222
+ >
223
+ <BrandMark size={32} />
224
+ <div className="min-w-0">
225
+ <div className="text-white text-[13px] font-semibold leading-tight">Agent Network</div>
226
+ <div className="text-[10px] text-gray-500 flex items-center gap-1.5 mt-0.5">
227
+ <span
228
+ className={`inline-block w-1.5 h-1.5 rounded-full ${online > 0 ? 'bg-emerald-400 anet-brand-pulse' : 'bg-gray-600'}`}
229
+ aria-hidden
230
+ />
231
+ {total === 0 ? (
232
+ <span>waiting for agents</span>
233
+ ) : (
234
+ <span><span className="text-gray-300 font-medium tabular-nums">{online}</span><span className="text-gray-600">/{total}</span> online</span>
235
+ )}
236
+ </div>
237
+ </div>
238
+ </Link>
239
+ );
240
+ }
241
+
242
+ /** 3-node mesh mark — same SVG as /login, reusable. */
243
+ function BrandMark({ size }: { size: number }) {
244
+ return (
245
+ <svg width={size} height={size} viewBox="0 0 32 32" aria-hidden className="shrink-0">
246
+ <circle cx="16" cy="16" r="10" fill="none" stroke="currentColor" strokeWidth="1.5" opacity="0.2" className="text-cyan-400" />
247
+ <line x1="16" y1="10" x2="10" y2="20" stroke="currentColor" strokeWidth="1" opacity="0.45" className="text-cyan-400" />
248
+ <line x1="16" y1="10" x2="22" y2="20" stroke="currentColor" strokeWidth="1" opacity="0.45" className="text-cyan-400" />
249
+ <line x1="10" y1="20" x2="22" y2="20" stroke="currentColor" strokeWidth="1" opacity="0.45" className="text-cyan-400" />
250
+ <circle cx="16" cy="10" r="2.5" fill="currentColor" className="text-cyan-400" />
251
+ <circle cx="10" cy="20" r="2.5" fill="currentColor" className="text-green-400" />
252
+ <circle cx="22" cy="20" r="2.5" fill="currentColor" className="text-violet-400" />
253
+ </svg>
254
+ );
255
+ }