@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
@@ -63,42 +63,123 @@ function truncate(value: string, max: number) {
63
63
  return value.length > max ? `${value.slice(0, max - 1)}...` : value;
64
64
  }
65
65
 
66
- function nodeStatus(session: Session, isOnline: boolean) {
66
+ function nodeStatus(session: Session, isOnline: boolean, isLight: boolean) {
67
67
  if (!isOnline) {
68
68
  return {
69
69
  label: 'offline',
70
- primary: '#6b7280',
71
- halo: '#111827',
72
- text: '#9ca3af',
70
+ primary: isLight ? '#94a3b8' : '#6b7280',
71
+ halo: isLight ? '#e2e8f0' : '#111827',
72
+ text: isLight ? '#475569' : '#9ca3af',
73
73
  };
74
74
  }
75
-
76
75
  if (session.status === 'working') {
77
76
  return {
78
77
  label: 'working',
79
- primary: '#22c55e',
80
- halo: '#14532d',
81
- text: '#dcfce7',
78
+ primary: isLight ? '#059669' : '#22c55e',
79
+ halo: isLight ? '#d1fae5' : '#14532d',
80
+ text: isLight ? '#065f46' : '#dcfce7',
82
81
  };
83
82
  }
84
-
85
83
  if (session.status === 'idle') {
86
84
  return {
87
85
  label: 'idle',
88
- primary: '#2dd4bf',
89
- halo: '#134e4a',
90
- text: '#ccfbf1',
86
+ primary: isLight ? '#0d9488' : '#2dd4bf',
87
+ halo: isLight ? '#ccfbf1' : '#134e4a',
88
+ text: isLight ? '#115e59' : '#ccfbf1',
91
89
  };
92
90
  }
93
-
94
91
  return {
95
92
  label: session.status || 'online',
96
- primary: '#38bdf8',
97
- halo: '#0c4a6e',
98
- text: '#e0f2fe',
93
+ primary: isLight ? '#0284c7' : '#38bdf8',
94
+ halo: isLight ? '#dbeafe' : '#0c4a6e',
95
+ text: isLight ? '#0c4a6e' : '#e0f2fe',
99
96
  };
100
97
  }
101
98
 
99
+ /** Theme-aware color palette for the topology SVG. */
100
+ interface Palette {
101
+ panelStops: [string, string, string];
102
+ radarStops: { color: string; opacity: number }[];
103
+ arrowFill: string;
104
+ ringStroke: string;
105
+ spokeStroke: { active: string; idle: string };
106
+ flowEdge: string;
107
+ flowPath: string;
108
+ flowParticle: string;
109
+ nodeFill: { online: string; offline: string };
110
+ labelBox: { fill: string; stroke: string };
111
+ legendBox: { fill: string; stroke: string };
112
+ legendText: string;
113
+ legendHeadline: string;
114
+ legendAccent: string;
115
+ containerBg: string;
116
+ containerBorder: string;
117
+ topRailGradient: string;
118
+ }
119
+
120
+ const DARK_PALETTE: Palette = {
121
+ panelStops: ['#0b1220', '#080814', '#101018'],
122
+ radarStops: [
123
+ { color: '#22d3ee', opacity: 0.18 },
124
+ { color: '#22c55e', opacity: 0.045 },
125
+ { color: '#020617', opacity: 0 },
126
+ ],
127
+ arrowFill: '#67e8f9',
128
+ ringStroke: '#164e63',
129
+ spokeStroke: { active: '#22d3ee', idle: '#155e75' },
130
+ flowEdge: '#67e8f9',
131
+ flowPath: '#e0f2fe',
132
+ flowParticle: '#fef08a',
133
+ nodeFill: { online: '#020617', offline: '#080814' },
134
+ labelBox: { fill: '#020617', stroke: '#1f2937' },
135
+ legendBox: { fill: '#020617', stroke: '#1f2937' },
136
+ legendText: '#94a3b8',
137
+ legendHeadline: '#e5e7eb',
138
+ legendAccent: '#67e8f9',
139
+ containerBg: '#080814',
140
+ containerBorder: '#2a2a4a',
141
+ topRailGradient: 'from-transparent via-cyan-400/70 to-transparent',
142
+ };
143
+
144
+ const LIGHT_PALETTE: Palette = {
145
+ panelStops: ['#f8fafc', '#ffffff', '#f1f5f9'],
146
+ radarStops: [
147
+ { color: '#10b981', opacity: 0.06 },
148
+ { color: '#3b82f6', opacity: 0.03 },
149
+ { color: '#ffffff', opacity: 0 },
150
+ ],
151
+ arrowFill: '#10b981',
152
+ ringStroke: '#cbd5e1',
153
+ spokeStroke: { active: '#10b981', idle: '#cbd5e1' },
154
+ flowEdge: '#10b981',
155
+ flowPath: '#475569',
156
+ flowParticle: '#f59e0b',
157
+ nodeFill: { online: '#ffffff', offline: '#f8fafc' },
158
+ labelBox: { fill: '#ffffff', stroke: '#e2e8f0' },
159
+ legendBox: { fill: '#ffffff', stroke: '#e2e8f0' },
160
+ legendText: '#475569',
161
+ legendHeadline: '#0f172a',
162
+ legendAccent: '#0d9488',
163
+ containerBg: '#ffffff',
164
+ containerBorder: '#e3e6eb',
165
+ topRailGradient: 'from-transparent via-emerald-500/40 to-transparent',
166
+ };
167
+
168
+ function useTheme(): 'light' | 'dark' {
169
+ const [theme, setTheme] = useState<'light' | 'dark'>('dark');
170
+ useEffect(() => {
171
+ const read = () => {
172
+ const t = document.documentElement.getAttribute('data-theme') || 'cyber';
173
+ setTheme(t === 'light' || t === 'mint' ? 'light' : 'dark');
174
+ };
175
+ read();
176
+ const obs = new MutationObserver(read);
177
+ obs.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] });
178
+ return () => obs.disconnect();
179
+ }, []);
180
+ return theme;
181
+ }
182
+
102
183
  function buildFlowLinks(messages: MessageFlow[], positions: Record<string, Point>) {
103
184
  const links = new Map<string, FlowLink>();
104
185
 
@@ -127,6 +208,9 @@ function buildFlowLinks(messages: MessageFlow[], positions: Record<string, Point
127
208
  }
128
209
 
129
210
  export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
211
+ const theme = useTheme();
212
+ const isLight = theme === 'light';
213
+ const pal = isLight ? LIGHT_PALETTE : DARK_PALETTE;
130
214
  const [messages, setMessages] = useState<MessageFlow[]>([]);
131
215
 
132
216
  useEffect(() => {
@@ -206,30 +290,35 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
206
290
  </div>
207
291
  </div>
208
292
 
209
- <div className="relative overflow-hidden rounded-lg border border-[#2a2a4a] bg-[#080814] shadow-2xl shadow-cyan-950/30">
210
- <div className="absolute inset-x-0 top-0 h-px bg-gradient-to-r from-transparent via-cyan-400/70 to-transparent" />
293
+ <div
294
+ className={`relative overflow-hidden rounded-lg border shadow-2xl ${isLight ? 'shadow-zinc-900/5' : 'shadow-cyan-950/30'}`}
295
+ style={{ background: pal.containerBg, borderColor: pal.containerBorder }}
296
+ >
297
+ <div className={`absolute inset-x-0 top-0 h-px bg-gradient-to-r ${pal.topRailGradient}`} />
211
298
 
212
299
  <svg viewBox="0 0 1000 680" className="w-full h-auto block" preserveAspectRatio="xMidYMid meet">
213
300
  <defs>
214
301
  <linearGradient id="topo-panel" x1="0" x2="1" y1="0" y2="1">
215
- <stop offset="0%" stopColor="#0b1220" />
216
- <stop offset="48%" stopColor="#080814" />
217
- <stop offset="100%" stopColor="#101018" />
302
+ <stop offset="0%" stopColor={pal.panelStops[0]} />
303
+ <stop offset="48%" stopColor={pal.panelStops[1]} />
304
+ <stop offset="100%" stopColor={pal.panelStops[2]} />
218
305
  </linearGradient>
219
306
  <radialGradient id="topo-radar" cx="50%" cy="50%" r="55%">
220
- <stop offset="0%" stopColor="#22d3ee" stopOpacity="0.18" />
221
- <stop offset="45%" stopColor="#22c55e" stopOpacity="0.045" />
222
- <stop offset="100%" stopColor="#020617" stopOpacity="0" />
307
+ <stop offset="0%" stopColor={pal.radarStops[0].color} stopOpacity={pal.radarStops[0].opacity} />
308
+ <stop offset="45%" stopColor={pal.radarStops[1].color} stopOpacity={pal.radarStops[1].opacity} />
309
+ <stop offset="100%" stopColor={pal.radarStops[2].color} stopOpacity={pal.radarStops[2].opacity} />
223
310
  </radialGradient>
224
- <filter id="topo-glow">
225
- <feGaussianBlur stdDeviation="4" result="blur" />
226
- <feMerge>
227
- <feMergeNode in="blur" />
228
- <feMergeNode in="SourceGraphic" />
229
- </feMerge>
230
- </filter>
311
+ {!isLight && (
312
+ <filter id="topo-glow">
313
+ <feGaussianBlur stdDeviation="4" result="blur" />
314
+ <feMerge>
315
+ <feMergeNode in="blur" />
316
+ <feMergeNode in="SourceGraphic" />
317
+ </feMerge>
318
+ </filter>
319
+ )}
231
320
  <marker id="topo-arrow" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="5" markerHeight="5" orient="auto-start-reverse">
232
- <path d="M 0 0 L 10 5 L 0 10 z" fill="#67e8f9" />
321
+ <path d="M 0 0 L 10 5 L 0 10 z" fill={pal.arrowFill} />
233
322
  </marker>
234
323
  </defs>
235
324
 
@@ -238,7 +327,7 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
238
327
 
239
328
  {/* radar rings */}
240
329
  {[90, 170, 250, 330].map(radius => (
241
- <circle key={radius} cx={cx} cy={cy} r={radius} fill="none" stroke="#164e63" strokeWidth="1" opacity="0.35" />
330
+ <circle key={radius} cx={cx} cy={cy} r={radius} fill="none" stroke={pal.ringStroke} strokeWidth="1" opacity={isLight ? 0.6 : 0.35} />
242
331
  ))}
243
332
  {[0, 30, 60, 90, 120, 150].map(angle => (
244
333
  <line
@@ -247,9 +336,9 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
247
336
  y1={cy - 360 * Math.sin(angle * Math.PI / 180)}
248
337
  x2={cx + 360 * Math.cos(angle * Math.PI / 180)}
249
338
  y2={cy + 360 * Math.sin(angle * Math.PI / 180)}
250
- stroke="#164e63"
339
+ stroke={pal.ringStroke}
251
340
  strokeWidth="1"
252
- opacity="0.18"
341
+ opacity={isLight ? 0.35 : 0.18}
253
342
  />
254
343
  ))}
255
344
 
@@ -264,7 +353,7 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
264
353
  key={`hub-${session.alias}`}
265
354
  d={path}
266
355
  fill="none"
267
- stroke={activeAliases.has(session.alias) ? '#22d3ee' : '#155e75'}
356
+ stroke={activeAliases.has(session.alias) ? pal.spokeStroke.active : pal.spokeStroke.idle}
268
357
  strokeWidth={activeAliases.has(session.alias) ? 2 : 1}
269
358
  strokeDasharray={activeAliases.has(session.alias) ? 'none' : '8 10'}
270
359
  opacity={activeAliases.has(session.alias) ? 0.65 : 0.35}
@@ -288,22 +377,22 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
288
377
  <path
289
378
  d={path}
290
379
  fill="none"
291
- stroke="#67e8f9"
380
+ stroke={pal.flowEdge}
292
381
  strokeWidth={width}
293
- opacity="0.28"
294
- filter="url(#topo-glow)"
382
+ opacity={isLight ? 0.22 : 0.28}
383
+ filter={isLight ? undefined : 'url(#topo-glow)'}
295
384
  markerEnd="url(#topo-arrow)"
296
385
  />
297
386
  <path
298
387
  id={`flow-path-${index}`}
299
388
  d={path}
300
389
  fill="none"
301
- stroke="#e0f2fe"
390
+ stroke={pal.flowPath}
302
391
  strokeWidth="1"
303
392
  strokeDasharray="2 12"
304
- opacity="0.75"
393
+ opacity={isLight ? 0.4 : 0.75}
305
394
  />
306
- <circle r="4" fill="#fef08a" filter="url(#topo-glow)">
395
+ <circle r="4" fill={pal.flowParticle} filter={isLight ? undefined : 'url(#topo-glow)'}>
307
396
  <animateMotion dur={`${duration}s`} repeatCount="indefinite" path={path} />
308
397
  </circle>
309
398
  </g>
@@ -340,38 +429,38 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
340
429
 
341
430
  const sseCountFor = (session.network_id ? sseSessions[`${session.network_id}:${session.alias}`] : undefined) ?? sseSessions[session.alias];
342
431
  const isOnline = session.status !== 'offline' || !!sseCountFor;
343
- const status = nodeStatus(session, isOnline);
432
+ const status = nodeStatus(session, isOnline, isLight);
344
433
  const isActive = activeAliases.has(session.alias);
345
434
  const radius = isOnline ? 26 : 18;
346
435
 
347
436
  return (
348
437
  <g key={session.alias} className="transition-opacity">
349
438
  {isActive && (
350
- <circle cx={pos.x} cy={pos.y} r={radius + 14} fill={status.primary} opacity="0.12">
439
+ <circle cx={pos.x} cy={pos.y} r={radius + 14} fill={status.primary} opacity={isLight ? 0.08 : 0.12}>
351
440
  <animate attributeName="r" values={`${radius + 8};${radius + 22};${radius + 8}`} dur="2.4s" repeatCount="indefinite" />
352
- <animate attributeName="opacity" values="0.18;0.04;0.18" dur="2.4s" repeatCount="indefinite" />
441
+ <animate attributeName="opacity" values={isLight ? '0.12;0.02;0.12' : '0.18;0.04;0.18'} dur="2.4s" repeatCount="indefinite" />
353
442
  </circle>
354
443
  )}
355
- <circle cx={pos.x} cy={pos.y} r={radius + 8} fill={status.halo} opacity={isOnline ? 0.55 : 0.25} />
444
+ <circle cx={pos.x} cy={pos.y} r={radius + 8} fill={status.halo} opacity={isOnline ? (isLight ? 0.85 : 0.55) : (isLight ? 0.4 : 0.25)} />
356
445
  <circle
357
446
  cx={pos.x}
358
447
  cy={pos.y}
359
448
  r={radius}
360
- fill={isOnline ? '#020617' : '#080814'}
449
+ fill={isOnline ? pal.nodeFill.online : pal.nodeFill.offline}
361
450
  stroke={status.primary}
362
451
  strokeWidth={isOnline ? 3 : 1.5}
363
452
  strokeDasharray={isOnline ? 'none' : '5 5'}
364
- filter={isOnline ? 'url(#topo-glow)' : undefined}
453
+ filter={isOnline && !isLight ? 'url(#topo-glow)' : undefined}
365
454
  />
366
455
  <circle cx={pos.x} cy={pos.y} r={isOnline ? 8 : 5} fill={status.primary} />
367
456
  {session.status === 'working' && (
368
- <path d={`M${pos.x - 9},${pos.y + 11} h18`} stroke="#fef08a" strokeWidth="3" strokeLinecap="round">
457
+ <path d={`M${pos.x - 9},${pos.y + 11} h18`} stroke={pal.flowParticle} strokeWidth="3" strokeLinecap="round">
369
458
  <animate attributeName="opacity" values="1;0.25;1" dur="1.1s" repeatCount="indefinite" />
370
459
  </path>
371
460
  )}
372
461
 
373
462
  <g transform={`translate(${pos.x}, ${pos.y + radius + 22})`}>
374
- <rect x="-62" y="-14" width="124" height="42" rx="6" fill="#020617" stroke="#1f2937" opacity="0.94" />
463
+ <rect x="-62" y="-14" width="124" height="42" rx="6" fill={pal.labelBox.fill} stroke={pal.labelBox.stroke} opacity={isLight ? 1 : 0.94} />
375
464
  <text x="0" y="1" textAnchor="middle" fill={status.text} fontSize="12" fontFamily="monospace" fontWeight="700">
376
465
  {truncate(session.alias, 14)}
377
466
  </text>
@@ -385,11 +474,11 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
385
474
 
386
475
  {/* latest flow labels */}
387
476
  <g transform="translate(28, 34)">
388
- <rect x="0" y="0" width="285" height={60 + Math.min(flowLinks.length, 4) * 20} rx="8" fill="#020617" stroke="#1f2937" opacity="0.94" />
389
- <text x="16" y="26" fill="#e5e7eb" fontSize="13" fontFamily="monospace" fontWeight="700">recent signal</text>
390
- <text x="178" y="26" fill="#67e8f9" fontSize="11" fontFamily="monospace">{messages.length} messages</text>
477
+ <rect x="0" y="0" width="285" height={60 + Math.min(flowLinks.length, 4) * 20} rx="8" fill={pal.legendBox.fill} stroke={pal.legendBox.stroke} opacity={isLight ? 1 : 0.94} />
478
+ <text x="16" y="26" fill={pal.legendHeadline} fontSize="13" fontFamily="monospace" fontWeight="700">recent signal</text>
479
+ <text x="178" y="26" fill={pal.legendAccent} fontSize="11" fontFamily="monospace">{messages.length} messages</text>
391
480
  {flowLinks.slice(0, 4).map((link, index) => (
392
- <text key={link.key} x="16" y={54 + index * 20} fill="#94a3b8" fontSize="10" fontFamily="monospace">
481
+ <text key={link.key} x="16" y={54 + index * 20} fill={pal.legendText} fontSize="10" fontFamily="monospace">
393
482
  {truncate(link.from, 8)} {'->'} {truncate(link.to, 8)} / {link.count} / {truncate(link.content, 18)}
394
483
  </text>
395
484
  ))}
@@ -397,14 +486,14 @@ export function TopoGraph({ sessions, sseSessions }: TopoGraphProps) {
397
486
 
398
487
  {/* legend */}
399
488
  <g transform="translate(720, 34)">
400
- <rect x="0" y="0" width="250" height="112" rx="8" fill="#020617" stroke="#1f2937" opacity="0.94" />
401
- <circle cx="18" cy="26" r="6" fill="#22c55e" />
402
- <text x="34" y="30" fill="#94a3b8" fontSize="11" fontFamily="monospace">working node</text>
403
- <circle cx="18" cy="52" r="6" fill="#2dd4bf" />
404
- <text x="34" y="56" fill="#94a3b8" fontSize="11" fontFamily="monospace">online idle</text>
405
- <circle cx="18" cy="78" r="6" fill="#6b7280" />
406
- <text x="34" y="82" fill="#94a3b8" fontSize="11" fontFamily="monospace">offline / no SSE</text>
407
- <path d="M150,78 Q176,52 210,78" fill="none" stroke="#67e8f9" strokeWidth="3" markerEnd="url(#topo-arrow)" />
489
+ <rect x="0" y="0" width="250" height="112" rx="8" fill={pal.legendBox.fill} stroke={pal.legendBox.stroke} opacity={isLight ? 1 : 0.94} />
490
+ <circle cx="18" cy="26" r="6" fill={isLight ? '#059669' : '#22c55e'} />
491
+ <text x="34" y="30" fill={pal.legendText} fontSize="11" fontFamily="monospace">working node</text>
492
+ <circle cx="18" cy="52" r="6" fill={isLight ? '#0d9488' : '#2dd4bf'} />
493
+ <text x="34" y="56" fill={pal.legendText} fontSize="11" fontFamily="monospace">online idle</text>
494
+ <circle cx="18" cy="78" r="6" fill={isLight ? '#94a3b8' : '#6b7280'} />
495
+ <text x="34" y="82" fill={pal.legendText} fontSize="11" fontFamily="monospace">offline / no SSE</text>
496
+ <path d="M150,78 Q176,52 210,78" fill="none" stroke={pal.flowEdge} strokeWidth="3" markerEnd="url(#topo-arrow)" />
408
497
  </g>
409
498
  </svg>
410
499
  </div>
@@ -140,8 +140,22 @@ export function UserBar() {
140
140
  ))}
141
141
  </select>
142
142
  )}
143
- <button onClick={() => { setEditName(auth.user?.display_name || ''); setEditEmail(''); setEditing(!editing); }} className="text-xs text-gray-500 hover:text-gray-300">Edit</button>
144
- <button onClick={logout} className="text-xs text-gray-500 hover:text-gray-300">Sign out</button>
143
+ <button onClick={() => { setEditName(auth.user?.display_name || ''); setEditEmail(''); setEditing(!editing); }}
144
+ className="text-xs text-gray-500 hover:text-gray-300 whitespace-nowrap"
145
+ aria-label="Edit profile">
146
+ <span className="hidden sm:inline">Edit</span>
147
+ <svg className="sm:hidden w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="1.5">
148
+ <path strokeLinecap="round" strokeLinejoin="round" d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.862 4.487z" />
149
+ </svg>
150
+ </button>
151
+ <button onClick={logout}
152
+ className="text-xs text-gray-500 hover:text-gray-300 whitespace-nowrap"
153
+ aria-label="Sign out">
154
+ <span className="hidden sm:inline">Sign out</span>
155
+ <svg className="sm:hidden w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="1.5">
156
+ <path strokeLinecap="round" strokeLinejoin="round" d="M15.75 9V5.25A2.25 2.25 0 0013.5 3h-6a2.25 2.25 0 00-2.25 2.25v13.5A2.25 2.25 0 007.5 21h6a2.25 2.25 0 002.25-2.25V15m3 0l3-3m0 0l-3-3m3 3H9" />
157
+ </svg>
158
+ </button>
145
159
  </div>
146
160
  {editing && (
147
161
  <div className="flex flex-wrap items-center gap-2 mt-2 pt-2 border-t border-[#2a2a4a]">
package/app/globals.css CHANGED
@@ -309,6 +309,66 @@ body {
309
309
  [data-theme="light"] .text-indigo-400 {
310
310
  color: var(--accent) !important;
311
311
  }
312
+
313
+ /* Light theme: every "-300/400" accent text color needs a darker mid-tone
314
+ variant. The default Tailwind `-300/-400` shades are designed for dark
315
+ backgrounds — putting them on white gives "barely visible" text (the
316
+ problem Vincent flagged: "浅色下很多字体看不见"). Map each family to its
317
+ `-700` equivalent so contrast against white card is ≥ AA. */
318
+ [data-theme="light"] .text-green-400,
319
+ [data-theme="light"] .text-green-300,
320
+ [data-theme="mint"] .text-green-400,
321
+ [data-theme="mint"] .text-green-300 { color: #047857 !important; }
322
+
323
+ [data-theme="light"] .text-yellow-400,
324
+ [data-theme="light"] .text-yellow-300,
325
+ [data-theme="mint"] .text-yellow-400,
326
+ [data-theme="mint"] .text-yellow-300 { color: #92400e !important; }
327
+
328
+ [data-theme="light"] .text-amber-400,
329
+ [data-theme="light"] .text-amber-300,
330
+ [data-theme="mint"] .text-amber-400,
331
+ [data-theme="mint"] .text-amber-300 { color: #92400e !important; }
332
+
333
+ [data-theme="light"] .text-red-400,
334
+ [data-theme="light"] .text-red-300,
335
+ [data-theme="mint"] .text-red-400,
336
+ [data-theme="mint"] .text-red-300 { color: #b91c1c !important; }
337
+
338
+ [data-theme="light"] .text-purple-400,
339
+ [data-theme="light"] .text-purple-300,
340
+ [data-theme="mint"] .text-purple-400,
341
+ [data-theme="mint"] .text-purple-300 { color: #6d28d9 !important; }
342
+
343
+ [data-theme="light"] .text-orange-400,
344
+ [data-theme="light"] .text-orange-300,
345
+ [data-theme="mint"] .text-orange-400,
346
+ [data-theme="mint"] .text-orange-300 { color: #c2410c !important; }
347
+
348
+ [data-theme="light"] .text-blue-400,
349
+ [data-theme="light"] .text-blue-300,
350
+ [data-theme="mint"] .text-blue-400,
351
+ [data-theme="mint"] .text-blue-300 { color: #1d4ed8 !important; }
352
+
353
+ [data-theme="light"] .text-pink-400,
354
+ [data-theme="light"] .text-pink-300,
355
+ [data-theme="mint"] .text-pink-400,
356
+ [data-theme="mint"] .text-pink-300 { color: #be185d !important; }
357
+
358
+ [data-theme="light"] .text-violet-400,
359
+ [data-theme="light"] .text-violet-300,
360
+ [data-theme="mint"] .text-violet-400,
361
+ [data-theme="mint"] .text-violet-300 { color: #6d28d9 !important; }
362
+
363
+ [data-theme="light"] .text-teal-400,
364
+ [data-theme="light"] .text-teal-300,
365
+ [data-theme="mint"] .text-teal-400,
366
+ [data-theme="mint"] .text-teal-300 { color: #0f766e !important; }
367
+
368
+ /* Light theme: text-gray-700 (used in footers / muted body) is too pale
369
+ when sitting on slightly-grey page bg. Bump to fg-muted. */
370
+ [data-theme="light"] .text-gray-700,
371
+ [data-theme="mint"] .text-gray-700 { color: var(--fg-muted) !important; }
312
372
  [data-theme="light"] .bg-emerald-500\/10,
313
373
  [data-theme="light"] .bg-emerald-500\/20,
314
374
  [data-theme="light"] .bg-cyan-500\/10,
@@ -517,6 +577,121 @@ body {
517
577
  box-shadow: 0 6px 18px -10px rgba(15, 23, 42, 0.12);
518
578
  }
519
579
 
580
+ /* ─────────────────────────────────────────────────────────────────────
581
+ Login surface (round 1 polish)
582
+ Restrained, minimal — first impression for new Agent Network users.
583
+ ─────────────────────────────────────────────────────────────────── */
584
+ .anet-login-bg {
585
+ background:
586
+ radial-gradient(circle at 50% 30%, rgba(34, 211, 238, 0.05), transparent 55%),
587
+ radial-gradient(circle at 33% 75%, rgba(59, 130, 246, 0.045), transparent 55%);
588
+ }
589
+ [data-theme="light"] .anet-login-bg,
590
+ [data-theme="mint"] .anet-login-bg {
591
+ background-image:
592
+ radial-gradient(circle at 50% 35%, rgba(0, 158, 126, 0.06), transparent 60%),
593
+ radial-gradient(circle, rgba(15, 23, 42, 0.05) 1px, transparent 1px);
594
+ background-size: 100% 100%, 24px 24px;
595
+ }
596
+ [data-theme="light"] .anet-login-mark,
597
+ [data-theme="mint"] .anet-login-mark {
598
+ background: var(--bg-secondary) !important;
599
+ box-shadow: 0 1px 2px rgba(15, 23, 42, 0.06), 0 0 0 1px var(--border);
600
+ }
601
+
602
+ /* Health banner — sticky thin strip above content, with theme-aware
603
+ green/amber/red tints. Dark themes use the lower-opacity bg+border
604
+ that come from the component classes directly. Light/mint need
605
+ slightly more saturation so the tint reads on a white surface. */
606
+ [data-theme="light"] .anet-health-banner.bg-emerald-500\/6,
607
+ [data-theme="mint"] .anet-health-banner.bg-emerald-500\/6 {
608
+ background-color: rgba(16, 185, 129, 0.08) !important;
609
+ color: #047857 !important;
610
+ border-color: rgba(16, 185, 129, 0.25) !important;
611
+ }
612
+ [data-theme="light"] .anet-health-banner.bg-amber-500\/8,
613
+ [data-theme="mint"] .anet-health-banner.bg-amber-500\/8 {
614
+ background-color: rgba(245, 158, 11, 0.12) !important;
615
+ color: #92400e !important;
616
+ border-color: rgba(245, 158, 11, 0.30) !important;
617
+ }
618
+ [data-theme="light"] .anet-health-banner.bg-red-500\/8,
619
+ [data-theme="mint"] .anet-health-banner.bg-red-500\/8 {
620
+ background-color: rgba(220, 38, 38, 0.10) !important;
621
+ color: #b91c1c !important;
622
+ border-color: rgba(220, 38, 38, 0.30) !important;
623
+ }
624
+
625
+ /* Sidebar brand "online" pulse — slow, restrained, NO glow.
626
+ 1.6s opacity drift between 1.0 and 0.5; nothing scales, nothing blurs. */
627
+ @keyframes anet-brand-pulse-kf {
628
+ 0%, 100% { opacity: 1; }
629
+ 50% { opacity: 0.4; }
630
+ }
631
+ .anet-brand-pulse {
632
+ animation: anet-brand-pulse-kf 1.6s ease-in-out infinite;
633
+ }
634
+ @media (prefers-reduced-motion: reduce) {
635
+ .anet-brand-pulse { animation: none; }
636
+ }
637
+
638
+ /* Skeleton pulse — same 1.6s rhythm as the brand pulse but applied to a
639
+ container so all bars inside breathe together. Drifts between full
640
+ token value and 60% so light themes show a calm grey-to-bg-elevated
641
+ fade and dark themes show a calm navy-to-elevated fade. */
642
+ @keyframes anet-skeleton-kf {
643
+ 0%, 100% { opacity: 1; }
644
+ 50% { opacity: 0.55; }
645
+ }
646
+ .anet-skeleton-pulse {
647
+ animation: anet-skeleton-kf 1.6s ease-in-out infinite;
648
+ }
649
+ @media (prefers-reduced-motion: reduce) {
650
+ .anet-skeleton-pulse { animation: none; }
651
+ }
652
+
653
+ /* Skeleton bar fill — different per theme so it's visible against the
654
+ card bg without being loud. Dark: lighter navy block on dark card.
655
+ Light: mid-grey on white card (not too light or it disappears). */
656
+ .anet-skeleton-bar {
657
+ background-color: #1a1a2a;
658
+ }
659
+ [data-theme="light"] .anet-skeleton-bar,
660
+ [data-theme="mint"] .anet-skeleton-bar {
661
+ background-color: #d4d8df;
662
+ }
663
+
664
+ /* Command palette modal surface — bumped depth so it sits clearly above
665
+ the page content in both themes. Light/mint: white shell with a real
666
+ shadow (not the dark navy that the CSS shim would normally translate
667
+ `bg-[#0d0d1a]` into). */
668
+ .anet-cmdk kbd {
669
+ font-size: 9px;
670
+ line-height: 1;
671
+ padding: 1px 4px;
672
+ border: 1px solid currentColor;
673
+ border-radius: 3px;
674
+ opacity: 0.6;
675
+ }
676
+ [data-theme="light"] .anet-cmdk > div:not([aria-hidden]),
677
+ [data-theme="mint"] .anet-cmdk > div:not([aria-hidden]) {
678
+ background-color: var(--bg-secondary) !important;
679
+ box-shadow: 0 10px 30px -8px rgba(15, 23, 42, 0.18),
680
+ 0 2px 6px -2px rgba(15, 23, 42, 0.10);
681
+ }
682
+ [data-theme="light"] .anet-cmdk-row.bg-cyan-500\/10,
683
+ [data-theme="mint"] .anet-cmdk-row.bg-cyan-500\/10 {
684
+ background-color: rgba(0, 158, 126, 0.08) !important;
685
+ }
686
+
687
+ /* Help overlay — same white-card-with-shadow treatment as cmdk on light */
688
+ [data-theme="light"] .anet-help > div:not([aria-hidden]),
689
+ [data-theme="mint"] .anet-help > div:not([aria-hidden]) {
690
+ background-color: var(--bg-secondary) !important;
691
+ box-shadow: 0 10px 30px -8px rgba(15, 23, 42, 0.18),
692
+ 0 2px 6px -2px rgba(15, 23, 42, 0.10);
693
+ }
694
+
520
695
  /* P0-3 baseline: error / warning chip colors for light themes.
521
696
  The component-level copy (replacing raw JSON with human text) is fixed
522
697
  in the Logs / Server-Logs page components separately. */
@@ -44,21 +44,32 @@ export default function LoginPage() {
44
44
  };
45
45
 
46
46
  return (
47
- <main className="min-h-screen bg-[#0a0a1a] text-gray-100 font-mono flex items-center justify-center relative overflow-hidden">
48
- <div className="absolute inset-0 pointer-events-none">
49
- <div className="absolute top-1/4 left-1/2 -translate-x-1/2 w-[600px] h-[600px] rounded-full bg-cyan-500/5 blur-[120px]" />
50
- <div className="absolute bottom-1/4 left-1/3 w-[400px] h-[400px] rounded-full bg-blue-500/5 blur-[100px]" />
51
- </div>
47
+ <main className="min-h-screen bg-[#0a0a1a] text-gray-100 font-mono flex items-center justify-center relative overflow-hidden px-4 py-10 sm:py-16">
48
+ {/* Subtle off-grid background — restraint over AI-glow.
49
+ Dark themes get a faint radial wash; light themes show a low-contrast
50
+ dotted grid for surface texture without noise. */}
51
+ <div className="absolute inset-0 pointer-events-none anet-login-bg" aria-hidden />
52
52
 
53
- <div className="relative w-full max-w-sm mx-4">
54
- <div className="text-center mb-8">
55
- <div className="inline-flex items-center justify-center w-16 h-16 rounded-2xl bg-gradient-to-br from-cyan-500/20 to-blue-500/20 border border-cyan-500/20 mb-4">
56
- <svg className="w-8 h-8 text-cyan-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
57
- <path strokeLinecap="round" strokeLinejoin="round" d="M3.75 6A2.25 2.25 0 016 3.75h2.25A2.25 2.25 0 0110.5 6v2.25a2.25 2.25 0 01-2.25 2.25H6a2.25 2.25 0 01-2.25-2.25V6zM3.75 15.75A2.25 2.25 0 016 13.5h2.25a2.25 2.25 0 012.25 2.25V18a2.25 2.25 0 01-2.25 2.25H6A2.25 2.25 0 013.75 18v-2.25zM13.5 6a2.25 2.25 0 012.25-2.25H18A2.25 2.25 0 0120.25 6v2.25A2.25 2.25 0 0118 10.5h-2.25a2.25 2.25 0 01-2.25-2.25V6zM13.5 15.75a2.25 2.25 0 012.25-2.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-2.25A2.25 2.25 0 0113.5 18v-2.25z" />
53
+ <div className="relative w-full max-w-sm">
54
+ <div className="text-center mb-7">
55
+ {/* Brand mark uses the favicon 3-node mesh SVG which IS the
56
+ agent-network concept (nodes + edges). Replaces the previous
57
+ flat emerald square placeholder. */}
58
+ <div className="inline-flex items-center justify-center w-16 h-16 mb-4 rounded-2xl bg-[#111128] border border-[#2a2a4a] anet-login-mark">
59
+ <svg className="w-9 h-9" viewBox="0 0 32 32" aria-hidden>
60
+ <circle cx="16" cy="16" r="10" fill="none" stroke="currentColor" strokeWidth="1.5" opacity="0.25" className="text-cyan-400 anet-login-mark-ring" />
61
+ <line x1="16" y1="10" x2="10" y2="20" stroke="currentColor" strokeWidth="1" opacity="0.5" className="text-cyan-400 anet-login-mark-edge" />
62
+ <line x1="16" y1="10" x2="22" y2="20" stroke="currentColor" strokeWidth="1" opacity="0.5" className="text-cyan-400 anet-login-mark-edge" />
63
+ <line x1="10" y1="20" x2="22" y2="20" stroke="currentColor" strokeWidth="1" opacity="0.5" className="text-cyan-400 anet-login-mark-edge" />
64
+ <circle cx="16" cy="10" r="3" fill="currentColor" className="text-cyan-400 anet-login-mark-node-a" />
65
+ <circle cx="10" cy="20" r="3" fill="currentColor" className="text-green-400 anet-login-mark-node-b" />
66
+ <circle cx="22" cy="20" r="3" fill="currentColor" className="text-violet-400 anet-login-mark-node-c" />
58
67
  </svg>
59
68
  </div>
60
- <h1 className="text-2xl font-bold text-white tracking-tight">Agent Network</h1>
61
- <p className="text-sm text-gray-500 mt-1">Dashboard</p>
69
+ <h1 className="text-[22px] sm:text-2xl font-semibold text-white tracking-tight">Agent Network</h1>
70
+ <p className="text-[13px] text-gray-500 mt-1.5 leading-snug">
71
+ One control plane for your AI agents · <span className="text-gray-600">Tasks · Mesh · Messages</span>
72
+ </p>
62
73
  </div>
63
74
 
64
75
  {/* Mode toggle */}
@@ -96,7 +107,9 @@ export default function LoginPage() {
96
107
  </button>
97
108
  </form>
98
109
 
99
- <p className="text-center text-xs text-gray-700 mt-6">Powered by Sleep2AGI</p>
110
+ <p className="text-center text-[11px] text-gray-700 mt-6">
111
+ Sleep2AGI · Apache-2.0 · <a href="https://anet.sh" className="hover:text-gray-500 transition-colors">anet.sh</a>
112
+ </p>
100
113
  </div>
101
114
  </main>
102
115
  );
package/app/logs/page.tsx CHANGED
@@ -4,6 +4,7 @@ import { useState, useEffect, useCallback } from 'react';
4
4
  import Link from 'next/link';
5
5
  import { timeAgo } from '../components/utils';
6
6
  import { useNetworkId } from '../lib/network-context';
7
+ import { EmptyState } from '../components/EmptyState';
7
8
 
8
9
  interface AuditLog {
9
10
  id: string;
@@ -121,11 +122,7 @@ export default function LogsPage() {
121
122
  {[1,2,3,4,5].map(i => <div key={i} className="h-14 bg-gray-800/20 rounded-lg" />)}
122
123
  </div>
123
124
  ) : logs.length === 0 ? (
124
- <div className="text-center py-16">
125
- <div className="text-gray-600 text-4xl mb-4">--</div>
126
- <h3 className="text-gray-400 text-lg font-medium mb-2">No audit logs</h3>
127
- <p className="text-gray-600 text-sm">Events will appear here when users register, login, or perform actions.</p>
128
- </div>
125
+ <EmptyState variant="logs" />
129
126
  ) : (
130
127
  <div className="space-y-2">
131
128
  {logs.map(log => (