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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (261) 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]__013i9e9._.js +9 -0
  162. package/.next/server/chunks/ssr/[root-of-the-server]__013i9e9._.js.map +1 -0
  163. package/.next/server/chunks/ssr/[root-of-the-server]__0_jnflc._.js +3 -0
  164. package/.next/server/chunks/ssr/[root-of-the-server]__0_jnflc._.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_login_page_tsx_0z71ad3._.js +1 -1
  180. package/.next/server/chunks/ssr/app_login_page_tsx_0z71ad3._.js.map +1 -1
  181. package/.next/server/chunks/ssr/app_page_tsx_0es_sk2._.js +1 -1
  182. package/.next/server/chunks/ssr/app_page_tsx_0es_sk2._.js.map +1 -1
  183. package/.next/server/chunks/ssr/app_tasks_page_tsx_0z-d6mp._.js +1 -1
  184. package/.next/server/chunks/ssr/app_tasks_page_tsx_0z-d6mp._.js.map +1 -1
  185. package/.next/server/chunks/ssr/node_modules_0ck2~9g._.js +3 -0
  186. package/.next/server/chunks/ssr/node_modules_0ck2~9g._.js.map +1 -0
  187. package/.next/server/middleware-build-manifest.js +6 -6
  188. package/.next/server/pages/404.html +2 -2
  189. package/.next/server/pages/500.html +1 -1
  190. package/.next/server/server-reference-manifest.js +1 -1
  191. package/.next/server/server-reference-manifest.json +1 -1
  192. package/.next/static/chunks/0257pdz1-imal.js +2 -0
  193. package/.next/static/chunks/02flrewxlf_m_.js +1 -0
  194. package/.next/static/chunks/051gslfjka1kj.js +1 -0
  195. package/.next/static/chunks/06t_vzei8s9_h.js +1 -0
  196. package/.next/static/chunks/0e-.zgw81gela.js +31 -0
  197. package/.next/static/chunks/0e7c40p~wq665.js +1 -0
  198. package/.next/static/chunks/0g7m7zk.v7g6h.js +1 -0
  199. package/.next/static/chunks/0hyhubmh-__wg.js +1 -0
  200. package/.next/static/chunks/0khpyxpdrp~ty.js +1 -0
  201. package/.next/static/chunks/0lo_g.q1fm-hr.js +7 -0
  202. package/.next/static/chunks/0snd~q2ucn.h1.css +2 -0
  203. package/.next/static/chunks/0w1zwt8kq_7nb.js +1 -0
  204. package/.next/static/chunks/0xn9.0ejo1s_a.js +7 -0
  205. package/.next/static/chunks/0y0lo-~k2c~00.js +1 -0
  206. package/.next/static/chunks/13.dst7liziwo.js +1 -0
  207. package/.next/static/chunks/{turbopack-0vsvjs02l449s.js → turbopack-01f1xs~cq_~si.js} +1 -1
  208. package/.next/trace +2 -2
  209. package/.next/trace-build +1 -1
  210. package/app/components/EmptyState.tsx +189 -0
  211. package/app/components/LoadingSkeleton.tsx +3 -18
  212. package/app/components/Sidebar.tsx +72 -11
  213. package/app/components/TopoGraph.tsx +151 -62
  214. package/app/globals.css +35 -0
  215. package/app/login/page.tsx +26 -13
  216. package/app/logs/page.tsx +2 -5
  217. package/app/messages/page.tsx +2 -5
  218. package/app/nodes/page.tsx +6 -1
  219. package/app/page.tsx +36 -12
  220. package/app/settings/networks/page.tsx +6 -1
  221. package/app/settings/page.tsx +56 -29
  222. package/app/settings/tokens/page.tsx +6 -1
  223. package/app/tasks/page.tsx +7 -9
  224. package/docs/loop-design-rounds.md +502 -0
  225. package/docs/oss-readiness-report.md +189 -0
  226. package/package.json +1 -1
  227. package/.next/server/chunks/ssr/[root-of-the-server]__093ymui._.js +0 -3
  228. package/.next/server/chunks/ssr/[root-of-the-server]__093ymui._.js.map +0 -1
  229. package/.next/server/chunks/ssr/[root-of-the-server]__0i1gizm._.js +0 -9
  230. package/.next/server/chunks/ssr/[root-of-the-server]__0i1gizm._.js.map +0 -1
  231. package/.next/server/chunks/ssr/[root-of-the-server]__0~yad5w._.js +0 -3
  232. package/.next/server/chunks/ssr/[root-of-the-server]__0~yad5w._.js.map +0 -1
  233. package/.next/server/chunks/ssr/_06_xh1~._.js +0 -3
  234. package/.next/server/chunks/ssr/_06_xh1~._.js.map +0 -1
  235. package/.next/server/chunks/ssr/app_01vhyfx._.js +0 -9
  236. package/.next/server/chunks/ssr/app_01vhyfx._.js.map +0 -1
  237. package/.next/server/chunks/ssr/app_023a94i._.js +0 -3
  238. package/.next/server/chunks/ssr/app_023a94i._.js.map +0 -1
  239. package/.next/server/chunks/ssr/app_0basiqb._.js +0 -3
  240. package/.next/server/chunks/ssr/app_0basiqb._.js.map +0 -1
  241. package/.next/server/chunks/ssr/app_0pljk5o._.js +0 -3
  242. package/.next/server/chunks/ssr/app_0pljk5o._.js.map +0 -1
  243. package/.next/server/chunks/ssr/node_modules_next_dist_0t-hj0x._.js +0 -3
  244. package/.next/server/chunks/ssr/node_modules_next_dist_0t-hj0x._.js.map +0 -1
  245. package/.next/static/chunks/0.z~9klrgd.t-.js +0 -1
  246. package/.next/static/chunks/08aj9~75raaah.js +0 -7
  247. package/.next/static/chunks/08iom_z8w-wmz.js +0 -7
  248. package/.next/static/chunks/08v0-ki6vwpxf.js +0 -1
  249. package/.next/static/chunks/0dkkv90do9a4r.js +0 -1
  250. package/.next/static/chunks/0e-wrhqh4h3r5.js +0 -2
  251. package/.next/static/chunks/0ejz4zwhu3a3y.js +0 -1
  252. package/.next/static/chunks/0o5zj~5vjeks3.js +0 -1
  253. package/.next/static/chunks/0uqfv6rc-xdru.js +0 -1
  254. package/.next/static/chunks/0u~_.32f.midj.css +0 -2
  255. package/.next/static/chunks/0xrnwfefpke-g.js +0 -31
  256. package/.next/static/chunks/0y_po~f~fk~ap.js +0 -1
  257. package/.next/static/chunks/14sm.ta0zylsj.js +0 -1
  258. package/.next/static/chunks/18ac93ii3mjlj.js +0 -1
  259. /package/.next/static/{jTh5m77FIfQccc0gftvik → 2m1yIfZCIYDWMYrrJEKSC}/_buildManifest.js +0 -0
  260. /package/.next/static/{jTh5m77FIfQccc0gftvik → 2m1yIfZCIYDWMYrrJEKSC}/_clientMiddlewareManifest.js +0 -0
  261. /package/.next/static/{jTh5m77FIfQccc0gftvik → 2m1yIfZCIYDWMYrrJEKSC}/_ssgManifest.js +0 -0
package/app/page.tsx CHANGED
@@ -8,7 +8,8 @@ import { BroadcastBar } from './components/BroadcastBar';
8
8
  import { TopoGraph } from './components/TopoGraph';
9
9
  import { AgentCard } from './components/AgentCard';
10
10
  import { InboxPanel } from './components/InboxPanel';
11
- import { LoadingSkeleton, EmptyState } from './components/LoadingSkeleton';
11
+ import { LoadingSkeleton } from './components/LoadingSkeleton';
12
+ import { NodesEmptyState as EmptyState } from './components/EmptyState';
12
13
  import { UserBar } from './components/UserBar';
13
14
  import { CommandCenter, useCommandCenter } from './components/CommandCenter';
14
15
  import { DispatchPanel } from './components/DispatchPanel';
@@ -185,19 +186,42 @@ export default function Dashboard() {
185
186
  </section>
186
187
  )}
187
188
 
188
- {/* Quick Actions */}
189
- <section className="mb-6 grid grid-cols-3 sm:grid-cols-6 gap-2">
189
+ {/* Quick Actions — split into two distinct intents:
190
+ (1) Top: live stat cards (carry data, drill-in on click)
191
+ (2) Bottom: pure nav rail (no number, icon + label)
192
+ Previously these were mixed in one row of 6, mixing data cells
193
+ ("0/0", "--", "0") with placeholder arrows ("→ Messages").
194
+ Users couldn't tell which were stats vs which were navigation. */}
195
+ <section className="mb-3 grid grid-cols-3 gap-2 sm:gap-3">
190
196
  {[
191
- { href: '/nodes', label: 'Nodes', value: `${online}/${total}`, color: 'text-green-400 border-green-500/15' },
192
- { href: '/tasks', label: 'Tasks', value: String(Object.values(taskStats).reduce((a, b) => a + b, 0) || '--'), color: 'text-cyan-400 border-cyan-500/15' },
193
- { href: '/tasks?status=failed', label: 'Failed', value: String(taskStats['failed'] || 0), color: taskStats['failed'] ? 'text-red-400 border-red-500/20' : 'text-gray-600 border-gray-700/30' },
194
- { href: '/messages', label: 'Messages', value: '→', color: 'text-blue-400 border-blue-500/15' },
195
- { href: '/logs', label: 'Logs', value: '→', color: 'text-purple-400 border-purple-500/15' },
196
- { href: '/admin', label: 'Admin', value: '→', color: 'text-gray-400 border-gray-500/15' },
197
+ { href: '/nodes', label: 'Nodes', value: `${online}/${total}`, sub: `${online > 0 ? Math.round((online/total)*100) : 0}% online`, color: 'text-green-400 border-green-500/20' },
198
+ { href: '/tasks', label: 'Tasks', value: String(Object.values(taskStats).reduce((a, b) => a + b, 0) || 0), sub: 'all-time', color: 'text-cyan-400 border-cyan-500/20' },
199
+ { href: '/tasks?status=failed', label: 'Failed', value: String(taskStats['failed'] || 0), sub: taskStats['failed'] ? 'needs review' : 'none', color: taskStats['failed'] ? 'text-red-400 border-red-500/25' : 'text-gray-500 border-gray-700/30' },
197
200
  ].map(a => (
198
- <Link key={a.href} href={a.href} prefetch={false} className={`text-center rounded-xl border ${a.color} bg-[#111128] px-3 py-2.5 hover:bg-[#161630] transition-colors`}>
199
- <div className={`text-lg font-bold tabular-nums ${a.color.split(' ')[0]}`}>{a.value}</div>
200
- <div className="text-[10px] text-gray-500">{a.label}</div>
201
+ <Link key={a.href} href={a.href} prefetch={false} className={`anet-stat-link group relative rounded-xl border ${a.color} bg-[#111128] px-3 py-3 transition-all hover:-translate-y-px`}>
202
+ <div className="flex items-baseline justify-between">
203
+ <div className={`text-xl font-semibold tabular-nums ${a.color.split(' ')[0]}`}>{a.value}</div>
204
+ <div className="text-[10px] text-gray-600 group-hover:text-gray-400 transition-colors">View →</div>
205
+ </div>
206
+ <div className="text-[11px] text-gray-400 mt-0.5">{a.label}</div>
207
+ <div className="text-[10px] text-gray-600 mt-px">{a.sub}</div>
208
+ </Link>
209
+ ))}
210
+ </section>
211
+
212
+ {/* Nav rail — pure navigation, icon + label, no data */}
213
+ <section className="mb-6 grid grid-cols-3 gap-2 sm:gap-3">
214
+ {[
215
+ { href: '/messages', label: 'Messages', icon: 'M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z' },
216
+ { href: '/logs', label: 'Audit log', icon: 'M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z M14 2v6h6 M16 13H8 M16 17H8 M10 9H8' },
217
+ { href: '/admin', label: 'Admin', icon: 'M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4z M6 21v-2a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v2' },
218
+ ].map(a => (
219
+ <Link key={a.href} href={a.href} prefetch={false}
220
+ className="anet-nav-tile flex items-center justify-center gap-2 rounded-xl border border-[#2a2a4a] bg-[#111128] px-3 py-2.5 text-[12px] text-gray-400 hover:text-gray-200 transition-colors">
221
+ <svg className="w-4 h-4 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
222
+ <path d={a.icon} />
223
+ </svg>
224
+ <span>{a.label}</span>
201
225
  </Link>
202
226
  ))}
203
227
  </section>
@@ -3,6 +3,7 @@
3
3
  import { useEffect, useState, useCallback } from 'react';
4
4
  import Link from 'next/link';
5
5
  import { timeAgo } from '../../components/utils';
6
+ import { EmptyState } from '../../components/EmptyState';
6
7
  import useSWR from 'swr';
7
8
 
8
9
  const fetcher = (url: string) => fetch(url).then(r => r.json());
@@ -126,7 +127,11 @@ export default function NetworksPage() {
126
127
  {loading ? (
127
128
  <div className="animate-pulse space-y-2">{[1,2].map(i => <div key={i} className="h-14 bg-gray-800/20 rounded" />)}</div>
128
129
  ) : networks.length === 0 ? (
129
- <div className="text-xs text-gray-600 text-center py-4">No networks found. Create one or sign in with V3 auth.</div>
130
+ <EmptyState
131
+ variant="networks"
132
+ compact
133
+ sub="Create your first network above, or sign in with V3 auth to see existing ones."
134
+ />
130
135
  ) : (
131
136
  <div className="space-y-3">
132
137
  {networks.map(n => (
@@ -20,7 +20,11 @@ export default function SettingsPage() {
20
20
  <div className="min-h-screen bg-[#0a0a1a] text-gray-100 p-4 sm:p-6 font-mono">
21
21
  <h1 className="text-2xl font-bold text-white mb-6 lg:ml-0 ml-10">Settings</h1>
22
22
 
23
- <div className="max-w-2xl space-y-6">
23
+ <div className="max-w-2xl space-y-8">
24
+ {/* ── Group: Connection ─────────────────────────────────── */}
25
+ <div className="space-y-4">
26
+ <div className="text-[10px] uppercase tracking-[0.12em] text-gray-600 px-1">Connection</div>
27
+
24
28
  {/* CommHub Connection */}
25
29
  <section className="bg-[#111128] border border-[#2a2a4a] rounded-xl p-5">
26
30
  <h2 className="text-sm font-semibold text-gray-300 mb-4">CommHub Connection</h2>
@@ -96,25 +100,41 @@ export default function SettingsPage() {
96
100
  </div>
97
101
  </div>
98
102
  </section>
103
+ </div>
104
+
105
+ {/* ── Group: Account ────────────────────────────────────── */}
106
+ <div className="space-y-4">
107
+ <div className="text-[10px] uppercase tracking-[0.12em] text-gray-600 px-1">Account</div>
99
108
 
100
109
  {/* License */}
101
110
  <section className="bg-[#111128] border border-[#2a2a4a] rounded-xl p-5">
102
- <h2 className="text-sm font-semibold text-gray-300 mb-4">License</h2>
111
+ <h2 className="text-sm font-semibold text-gray-300 mb-4 flex items-center gap-2">
112
+ License
113
+ {licData?.license && (
114
+ <span
115
+ className={`inline-flex items-center gap-1.5 text-[10px] font-medium px-2 py-0.5 rounded-full border ${
116
+ licData.license.type === 'pro'
117
+ ? 'text-green-300 bg-green-500/10 border-green-500/30'
118
+ : licData.license.days_left <= 7
119
+ ? 'text-red-300 bg-red-500/10 border-red-500/30'
120
+ : 'text-amber-300 bg-amber-500/10 border-amber-500/30'
121
+ }`}
122
+ >
123
+ <span aria-hidden className="w-1.5 h-1.5 rounded-full bg-current" />
124
+ {licData.license.type}{licData.license.days_left ? ` · ${licData.license.days_left}d left` : ''}
125
+ </span>
126
+ )}
127
+ </h2>
103
128
  {licData?.license ? (
104
129
  <div className="space-y-3 text-sm">
105
- <div className={rowClass}>
106
- <span className="text-gray-500">Type</span>
107
- <span className={`font-medium ${licData.license.type === 'pro' ? 'text-green-400' : 'text-yellow-400'}`}>
108
- {licData.license.type}
109
- </span>
110
- </div>
111
- <div className={rowClass}>
112
- <span className="text-gray-500">Days Left</span>
113
- <span className={`${licData.license.days_left <= 7 ? 'text-red-400' : 'text-gray-300'}`}>
114
- {licData.license.days_left} days
115
- {licData.license.days_left <= 7 && ' — expiring soon!'}
116
- </span>
117
- </div>
130
+ {/* Type + Days Left are summarized in the inline chip in the
131
+ header. Surface "expiring soon" only when relevant. */}
132
+ {licData.license.days_left <= 7 && (
133
+ <div className={rowClass}>
134
+ <span className="text-red-400 font-medium">⚠ Expiring soon</span>
135
+ <span className="text-red-400">{licData.license.days_left} days left</span>
136
+ </div>
137
+ )}
118
138
  <div className={rowClass}>
119
139
  <span className="text-gray-500">Expires</span>
120
140
  <span className={`text-gray-300 ${valueClass}`}>{licData.license.expires_at}</span>
@@ -195,6 +215,26 @@ export default function SettingsPage() {
195
215
  {pwdResult && <div className={`mt-2 text-xs ${pwdResult.startsWith('Failed') ? 'text-red-400' : 'text-green-400'}`}>{pwdResult}</div>}
196
216
  </section>
197
217
 
218
+ {/* Session — tone-neutral, no longer "danger zone" red */}
219
+ <section className="bg-[#111128] border border-[#2a2a4a] rounded-xl p-5">
220
+ <h2 className="text-sm font-semibold text-gray-300 mb-4">Sign out</h2>
221
+ <p className="text-xs text-gray-500 mb-3">Signing out clears your dashboard session cookie. You'll return to the login page.</p>
222
+ <button
223
+ onClick={async () => {
224
+ await fetch('/api/auth/logout', { method: 'POST' }).catch(() => {});
225
+ window.location.assign('/login');
226
+ }}
227
+ className="px-4 py-2 bg-transparent hover:bg-[#1a1a2a] text-gray-300 text-sm rounded-lg border border-[#2a2a4a] hover:border-[#3a3a5a] transition-colors"
228
+ >
229
+ Sign out
230
+ </button>
231
+ </section>
232
+ </div>
233
+
234
+ {/* ── Group: Resources ──────────────────────────────────── */}
235
+ <div className="space-y-4">
236
+ <div className="text-[10px] uppercase tracking-[0.12em] text-gray-600 px-1">Resources</div>
237
+
198
238
  {/* API Tokens + Networks */}
199
239
  <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
200
240
  <Link href="/settings/tokens" className="bg-[#111128] border border-[#2a2a4a] rounded-xl p-5 hover:border-cyan-500/30 transition-colors">
@@ -208,21 +248,8 @@ export default function SettingsPage() {
208
248
  <span className="text-xs text-cyan-400 mt-3 inline-block">Manage &rarr;</span>
209
249
  </Link>
210
250
  </div>
251
+ </div>
211
252
 
212
- {/* Danger Zone */}
213
- <section className="bg-[#111128] border border-red-900/30 rounded-xl p-5">
214
- <h2 className="text-sm font-semibold text-red-400 mb-4">Session</h2>
215
- <p className="text-xs text-gray-500 mb-3">Sign out will clear your dashboard session cookie.</p>
216
- <button
217
- onClick={async () => {
218
- await fetch('/api/auth/logout', { method: 'POST' }).catch(() => {});
219
- window.location.assign('/login');
220
- }}
221
- className="px-4 py-2 bg-red-600/20 hover:bg-red-600/30 text-red-300 text-sm rounded-lg border border-red-800/30 transition-colors"
222
- >
223
- Sign out
224
- </button>
225
- </section>
226
253
  </div>
227
254
  </div>
228
255
  );
@@ -3,6 +3,7 @@
3
3
  import { useEffect, useState, useCallback } from 'react';
4
4
  import Link from 'next/link';
5
5
  import { timeAgo } from '../../components/utils';
6
+ import { EmptyState } from '../../components/EmptyState';
6
7
 
7
8
  interface Token {
8
9
  token_id: string;
@@ -100,7 +101,11 @@ export default function TokensPage() {
100
101
  {loading ? (
101
102
  <div className="animate-pulse space-y-2">{[1,2].map(i => <div key={i} className="h-12 bg-gray-800/20 rounded" />)}</div>
102
103
  ) : tokens.length === 0 ? (
103
- <div className="text-xs text-gray-600 text-center py-4">No tokens. V3 auth required.</div>
104
+ <EmptyState
105
+ variant="tokens"
106
+ compact
107
+ sub="Tokens authenticate CLI tools and external integrations. Sign in with V3 auth, then create one above."
108
+ />
104
109
  ) : (
105
110
  <div className="space-y-2">
106
111
  {tokens.map(t => (
@@ -5,6 +5,7 @@ import { useSearchParams } from 'next/navigation';
5
5
  import Link from 'next/link';
6
6
  import { useNetworkId } from '../lib/network-context';
7
7
  import { TaskDrawer } from '../components/TaskDrawer';
8
+ import { EmptyState } from '../components/EmptyState';
8
9
 
9
10
  interface Task {
10
11
  task_id: string;
@@ -201,15 +202,12 @@ function TasksContent() {
201
202
  ))}
202
203
  </div>
203
204
  ) : tasks.length === 0 ? (
204
- <div className="text-center py-16">
205
- <div className="text-gray-600 text-4xl mb-4">--</div>
206
- <h3 className="text-gray-400 text-lg font-medium mb-2">No tasks found</h3>
207
- <p className="text-gray-600 text-sm">
208
- {filterStatus || filterFrom || filterTo
209
- ? 'Try adjusting your filters.'
210
- : 'Tasks will appear here when agents send them via CommHub.'}
211
- </p>
212
- </div>
205
+ <EmptyState
206
+ variant="tasks"
207
+ sub={(filterStatus || filterFrom || filterTo)
208
+ ? 'No tasks match the current filters. Try clearing them.'
209
+ : 'Tasks will appear here when agents send them via CommHub.'}
210
+ />
213
211
  ) : (
214
212
  <div className="space-y-2">
215
213
  {/* Table header */}