@sleep2agi/agent-network-dashboard 0.5.7-preview.2 → 0.5.7-preview.21

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 (226) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +3 -3
  3. package/.next/diagnostics/route-bundle-stats.json +46 -46
  4. package/.next/fallback-build-manifest.json +3 -3
  5. package/.next/prerender-manifest.json +3 -3
  6. package/.next/server/app/_global-error.html +1 -1
  7. package/.next/server/app/_global-error.rsc +1 -1
  8. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  9. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  12. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  14. package/.next/server/app/_not-found.html +3 -3
  15. package/.next/server/app/_not-found.rsc +13 -13
  16. package/.next/server/app/_not-found.segments/_full.segment.rsc +13 -13
  17. package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  18. package/.next/server/app/_not-found.segments/_index.segment.rsc +8 -8
  19. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  20. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  21. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  22. package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
  23. package/.next/server/app/admin.html +3 -3
  24. package/.next/server/app/admin.rsc +15 -15
  25. package/.next/server/app/admin.segments/_full.segment.rsc +15 -15
  26. package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
  27. package/.next/server/app/admin.segments/_index.segment.rsc +8 -8
  28. package/.next/server/app/admin.segments/_tree.segment.rsc +2 -2
  29. package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +4 -4
  30. package/.next/server/app/admin.segments/admin.segment.rsc +3 -3
  31. package/.next/server/app/index.html +3 -3
  32. package/.next/server/app/index.rsc +15 -15
  33. package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
  34. package/.next/server/app/index.segments/_full.segment.rsc +15 -15
  35. package/.next/server/app/index.segments/_head.segment.rsc +4 -4
  36. package/.next/server/app/index.segments/_index.segment.rsc +8 -8
  37. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  38. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  39. package/.next/server/app/login.html +2 -2
  40. package/.next/server/app/login.rsc +15 -15
  41. package/.next/server/app/login.segments/_full.segment.rsc +15 -15
  42. package/.next/server/app/login.segments/_head.segment.rsc +4 -4
  43. package/.next/server/app/login.segments/_index.segment.rsc +8 -8
  44. package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
  45. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
  46. package/.next/server/app/login.segments/login.segment.rsc +3 -3
  47. package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
  48. package/.next/server/app/logs.html +3 -3
  49. package/.next/server/app/logs.rsc +15 -15
  50. package/.next/server/app/logs.segments/_full.segment.rsc +15 -15
  51. package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
  52. package/.next/server/app/logs.segments/_index.segment.rsc +8 -8
  53. package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
  54. package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
  55. package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
  56. package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
  57. package/.next/server/app/messages.html +3 -3
  58. package/.next/server/app/messages.rsc +15 -15
  59. package/.next/server/app/messages.segments/_full.segment.rsc +15 -15
  60. package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
  61. package/.next/server/app/messages.segments/_index.segment.rsc +8 -8
  62. package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
  63. package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
  64. package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
  65. package/.next/server/app/node/page_client-reference-manifest.js +1 -1
  66. package/.next/server/app/node.html +3 -3
  67. package/.next/server/app/node.rsc +15 -15
  68. package/.next/server/app/node.segments/_full.segment.rsc +15 -15
  69. package/.next/server/app/node.segments/_head.segment.rsc +4 -4
  70. package/.next/server/app/node.segments/_index.segment.rsc +8 -8
  71. package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
  72. package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
  73. package/.next/server/app/node.segments/node.segment.rsc +3 -3
  74. package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
  75. package/.next/server/app/nodes.html +3 -3
  76. package/.next/server/app/nodes.rsc +15 -15
  77. package/.next/server/app/nodes.segments/_full.segment.rsc +15 -15
  78. package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
  79. package/.next/server/app/nodes.segments/_index.segment.rsc +8 -8
  80. package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
  81. package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
  82. package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
  83. package/.next/server/app/page_client-reference-manifest.js +1 -1
  84. package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
  85. package/.next/server/app/server-logs.html +3 -3
  86. package/.next/server/app/server-logs.rsc +15 -15
  87. package/.next/server/app/server-logs.segments/_full.segment.rsc +15 -15
  88. package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
  89. package/.next/server/app/server-logs.segments/_index.segment.rsc +8 -8
  90. package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
  91. package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
  92. package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
  93. package/.next/server/app/servers/page_client-reference-manifest.js +1 -1
  94. package/.next/server/app/servers.html +3 -3
  95. package/.next/server/app/servers.rsc +15 -15
  96. package/.next/server/app/servers.segments/_full.segment.rsc +15 -15
  97. package/.next/server/app/servers.segments/_head.segment.rsc +4 -4
  98. package/.next/server/app/servers.segments/_index.segment.rsc +8 -8
  99. package/.next/server/app/servers.segments/_tree.segment.rsc +2 -2
  100. package/.next/server/app/servers.segments/servers/__PAGE__.segment.rsc +4 -4
  101. package/.next/server/app/servers.segments/servers.segment.rsc +3 -3
  102. package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
  103. package/.next/server/app/settings/networks.html +3 -3
  104. package/.next/server/app/settings/networks.rsc +15 -15
  105. package/.next/server/app/settings/networks.segments/_full.segment.rsc +15 -15
  106. package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
  107. package/.next/server/app/settings/networks.segments/_index.segment.rsc +8 -8
  108. package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
  109. package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
  110. package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
  111. package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
  112. package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  113. package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
  114. package/.next/server/app/settings/tokens.html +3 -3
  115. package/.next/server/app/settings/tokens.rsc +15 -15
  116. package/.next/server/app/settings/tokens.segments/_full.segment.rsc +15 -15
  117. package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
  118. package/.next/server/app/settings/tokens.segments/_index.segment.rsc +8 -8
  119. package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
  120. package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
  121. package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
  122. package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
  123. package/.next/server/app/settings.html +3 -3
  124. package/.next/server/app/settings.rsc +15 -15
  125. package/.next/server/app/settings.segments/_full.segment.rsc +15 -15
  126. package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
  127. package/.next/server/app/settings.segments/_index.segment.rsc +8 -8
  128. package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  129. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
  130. package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
  131. package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
  132. package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
  133. package/.next/server/app/tasks.html +3 -3
  134. package/.next/server/app/tasks.rsc +15 -15
  135. package/.next/server/app/tasks.segments/_full.segment.rsc +15 -15
  136. package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
  137. package/.next/server/app/tasks.segments/_index.segment.rsc +8 -8
  138. package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
  139. package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
  140. package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
  141. package/.next/server/chunks/ssr/[root-of-the-server]__030vg4n._.js +1 -1
  142. package/.next/server/chunks/ssr/[root-of-the-server]__030vg4n._.js.map +1 -1
  143. package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js +1 -1
  144. package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js.map +1 -1
  145. package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js +1 -1
  146. package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js.map +1 -1
  147. package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js +1 -1
  148. package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js.map +1 -1
  149. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +2 -2
  150. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
  151. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
  152. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
  153. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
  154. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
  155. package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js +1 -1
  156. package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js.map +1 -1
  157. package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js +1 -1
  158. package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js.map +1 -1
  159. package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js +1 -1
  160. package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js.map +1 -1
  161. package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js +1 -1
  162. package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js.map +1 -1
  163. package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js +1 -1
  164. package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js.map +1 -1
  165. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0s5uqlp._.js +2 -2
  166. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0s5uqlp._.js.map +1 -1
  167. package/.next/server/chunks/ssr/agent-network-dashboard_app_server-logs_page_tsx_0dg.l_8._.js +1 -1
  168. package/.next/server/chunks/ssr/agent-network-dashboard_app_server-logs_page_tsx_0dg.l_8._.js.map +1 -1
  169. package/.next/server/chunks/ssr/agent-network-dashboard_app_tasks_page_tsx_0mwxy4z._.js +1 -1
  170. package/.next/server/chunks/ssr/agent-network-dashboard_app_tasks_page_tsx_0mwxy4z._.js.map +1 -1
  171. package/.next/server/middleware-build-manifest.js +3 -3
  172. package/.next/server/pages/404.html +3 -3
  173. package/.next/server/pages/500.html +1 -1
  174. package/.next/server/server-reference-manifest.js +1 -1
  175. package/.next/server/server-reference-manifest.json +1 -1
  176. package/.next/static/chunks/{08sos26.n11bq.js → 0-1p~7bijv8r0.js} +1 -1
  177. package/.next/static/chunks/0.054rbp43q.y.js +1 -0
  178. package/.next/static/chunks/{181u38qblp8lz.js → 00b-ysl~m~dr~.js} +1 -1
  179. package/.next/static/chunks/{0jp~cs9-zkmqa.js → 00b4y77vxfabl.js} +1 -1
  180. package/.next/static/chunks/{0561vp5-q5.zp.js → 05wpkysi1s2zn.js} +1 -1
  181. package/.next/static/chunks/06iwj-z8xtcq3.js +7 -0
  182. package/.next/static/chunks/089vil3mpokhi.js +1 -0
  183. package/.next/static/chunks/{0a.9~-nf0gpec.js → 0_eddxjio~tei.js} +1 -1
  184. package/.next/static/chunks/0fzfrj9t-fbn_.js +1 -0
  185. package/.next/static/chunks/{15qxef.ilfysw.js → 0htz_k0~gd-wb.js} +3 -3
  186. package/.next/static/chunks/{0im751o4n61c7.js → 0hv6izw.g6cnm.js} +1 -1
  187. package/.next/static/chunks/0scdduv38l5yh.js +1 -0
  188. package/.next/static/chunks/{05uk96gc~9mni.js → 0ti3v67ixu43p.js} +1 -1
  189. package/.next/static/chunks/0zxp22qnae87..css +1 -0
  190. package/.next/static/chunks/{0.66f3.rtcybb.js → 136r0ae9ihgvo.js} +1 -1
  191. package/.next/static/chunks/{0nqm.7w9_inwd.js → 15yjjo1fv.muz.js} +2 -2
  192. package/.next/trace +2 -2
  193. package/.next/trace-build +1 -1
  194. package/app/admin/page.tsx +9 -4
  195. package/app/components/AgentCard.tsx +19 -9
  196. package/app/components/ChatPopover.tsx +1 -1
  197. package/app/components/CommandCenter.tsx +12 -2
  198. package/app/components/CommandPalette.tsx +1 -1
  199. package/app/components/DispatchPanel.tsx +7 -4
  200. package/app/components/HealthBanner.tsx +10 -2
  201. package/app/components/MobileNav.tsx +2 -2
  202. package/app/components/Sidebar.tsx +6 -4
  203. package/app/components/TaskChatPanel.tsx +19 -5
  204. package/app/components/TaskDrawer.tsx +3 -1
  205. package/app/components/UserBar.tsx +3 -3
  206. package/app/login/page.tsx +3 -3
  207. package/app/logs/page.tsx +6 -2
  208. package/app/messages/page.tsx +19 -12
  209. package/app/node/page.tsx +2 -2
  210. package/app/nodes/page.tsx +12 -6
  211. package/app/server-logs/page.tsx +10 -3
  212. package/app/settings/networks/page.tsx +3 -3
  213. package/app/settings/page.tsx +8 -5
  214. package/app/settings/tokens/page.tsx +1 -1
  215. package/app/tasks/page.tsx +13 -9
  216. package/bin/start.js +0 -0
  217. package/package.json +1 -1
  218. package/.next/static/chunks/0hxvojbn.tprh.css +0 -1
  219. package/.next/static/chunks/0inql3s9ldyx5.js +0 -1
  220. package/.next/static/chunks/0yyx7754lietg.js +0 -1
  221. package/.next/static/chunks/11xnnivocgef4.js +0 -1
  222. package/.next/static/chunks/12-6xsdci5.9v.js +0 -1
  223. package/.next/static/chunks/15-ltfhot3b4n.js +0 -7
  224. /package/.next/static/{D1tOdIVZJAhZcHTEAKrHD → bsQFZ0KSnvC1MnJVkif4H}/_buildManifest.js +0 -0
  225. /package/.next/static/{D1tOdIVZJAhZcHTEAKrHD → bsQFZ0KSnvC1MnJVkif4H}/_clientMiddlewareManifest.js +0 -0
  226. /package/.next/static/{D1tOdIVZJAhZcHTEAKrHD → bsQFZ0KSnvC1MnJVkif4H}/_ssgManifest.js +0 -0
@@ -91,10 +91,12 @@ export function Sidebar() {
91
91
 
92
92
  return (
93
93
  <>
94
- {/* Mobile hamburger */}
94
+ {/* Mobile hamburger — R13 of #190: was p-2.5 = ~40px tap target,
95
+ just below the iOS 44px guideline. Bump padding and add an
96
+ explicit min-w/min-h so it can never be miss-tapped. */}
95
97
  <button
96
98
  onClick={() => setMobileOpen(!mobileOpen)}
97
- className="fixed top-4 left-3 z-50 lg:hidden bg-[#111128] border border-[#2a2a4a] rounded-lg p-2.5 text-gray-400 hover:text-white active:bg-[#1a1a3a]"
99
+ className="fixed top-3 left-3 z-50 lg:hidden bg-[#111128] border border-[#2a2a4a] rounded-lg p-3 min-h-[44px] min-w-[44px] inline-flex items-center justify-center text-gray-400 hover:text-white active:bg-[#1a1a3a]"
98
100
  aria-label="Toggle menu"
99
101
  >
100
102
  <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
@@ -143,7 +145,7 @@ export function Sidebar() {
143
145
  key={n.network_id}
144
146
  onClick={() => { setNetworkId(n.network_id); setMobileOpen(false); }}
145
147
  title={n.network_id}
146
- className={`w-full flex items-center gap-2 px-3 py-1.5 rounded-md text-xs transition-colors text-left ${
148
+ className={`w-full flex items-center gap-2 px-3 py-2.5 lg:py-1.5 rounded-md text-xs transition-colors text-left ${
147
149
  networkId === n.network_id
148
150
  ? 'bg-cyan-500/10 text-cyan-300'
149
151
  : 'text-gray-500 hover:text-gray-300 hover:bg-[#1a1a2a]'
@@ -178,7 +180,7 @@ export function Sidebar() {
178
180
  }}
179
181
  title={collapsed ? 'Quick search (⌘K)' : undefined}
180
182
  className={`w-full flex items-center text-[11px] text-gray-600 hover:text-gray-400 hover:bg-[#1a1a2a] transition-colors ${
181
- collapsed ? 'justify-center px-0 py-2.5' : 'justify-between gap-2 px-5 py-2'
183
+ collapsed ? 'justify-center px-0 py-2.5' : 'justify-between gap-2 px-5 py-3 lg:py-2'
182
184
  }`}
183
185
  aria-label="Open command palette"
184
186
  >
@@ -483,7 +483,14 @@ export function TaskChatPanel({ alias, onClose, inline, availableNodes }: TaskCh
483
483
  const chatContent = (
484
484
  <>
485
485
  {/* Messages area */}
486
- <div className="flex-1 overflow-y-auto px-3 py-3 sm:px-4 sm:py-4 space-y-3 sm:space-y-4">
486
+ {/* R12 of #190 mobile polish: the chat scroll surface used
487
+ space-y-3 between task+reply pairs at mobile = 12 px, which
488
+ in a long thread (the panel's bread-and-butter use case)
489
+ adds up to a significant scroll length. Drop to space-y-2 at
490
+ mobile and the per-pair grouping (line 540) from space-y-2
491
+ to space-y-1.5 so messages read denser without losing the
492
+ speaker-turn rhythm. Desktop unchanged at sm: and up. */}
493
+ <div className="flex-1 overflow-y-auto px-3 py-3 sm:px-4 sm:py-4 space-y-2 sm:space-y-4">
487
494
  {!historyLoaded && (
488
495
  <div className="flex justify-center py-8">
489
496
  <div className="w-5 h-5 border-2 border-cyan-500/30 border-t-cyan-500 rounded-full animate-spin" />
@@ -537,7 +544,7 @@ export function TaskChatPanel({ alias, onClose, inline, availableNodes }: TaskCh
537
544
  );
538
545
  }
539
546
  return (
540
- <div key={`${m.task_id}:task`} className="space-y-2">
547
+ <div key={`${m.task_id}:task`} className="space-y-1.5 sm:space-y-2">
541
548
  {/* Outgoing task — labeled with origin so peer-forwarded tasks are obvious */}
542
549
  <div className="flex justify-end">
543
550
  <div className="max-w-[92%] sm:max-w-[85%] bg-cyan-500/8 border border-cyan-500/15 rounded-2xl rounded-br-md px-3 py-2.5 sm:px-4 shadow-sm">
@@ -626,8 +633,11 @@ export function TaskChatPanel({ alias, onClose, inline, availableNodes }: TaskCh
626
633
  </select>
627
634
  </div>
628
635
  </div>
629
- <button onClick={send} disabled={sending || (!input.trim() && attachedFiles.length === 0)}
630
- className="p-2.5 bg-cyan-600 hover:bg-cyan-500 disabled:bg-[var(--border)] disabled:text-[var(--fg-dim)] text-[var(--fg)] rounded-xl transition-all shrink-0 active:scale-95">
636
+ {/* R17 of #190: send button was p-2.5 + w-5 icon = ~40 x 40
637
+ hit zone, 4 px short of the iOS 44 px guideline on
638
+ the short axis. Bump to inline-flex + min-h/w 44. */}
639
+ <button onClick={send} aria-label="Send message" disabled={sending || (!input.trim() && attachedFiles.length === 0)}
640
+ className="inline-flex min-h-[44px] min-w-[44px] items-center justify-center bg-cyan-600 hover:bg-cyan-500 disabled:bg-[var(--border)] disabled:text-[var(--fg-dim)] text-[var(--fg)] rounded-xl transition-all shrink-0 active:scale-95">
631
641
  {sending ? (
632
642
  <div className="w-5 h-5 border-2 border-white/30 border-t-white rounded-full animate-spin" />
633
643
  ) : (
@@ -673,7 +683,11 @@ export function TaskChatPanel({ alias, onClose, inline, availableNodes }: TaskCh
673
683
  <div className="text-[10px] text-[var(--fg-muted)]">{pollingIds.size > 0 ? 'Processing...' : 'Ready'}</div>
674
684
  </div>
675
685
  </div>
676
- <button onClick={onClose} className="text-[var(--fg-muted)] hover:text-[var(--fg)] p-1.5 rounded-lg hover:bg-[var(--bg-elevated)]">
686
+ {/* R16 of #190: was p-1.5 + w-5 h-5 svg = ~32 px tap target.
687
+ The chat panel close is high-frequency on mobile (user
688
+ dismisses to scroll the underlying page); bump to a
689
+ uniform 44 x 44 hit zone via inline-flex + min-h/w. */}
690
+ <button onClick={onClose} aria-label="Close chat" className="inline-flex min-h-[44px] min-w-[44px] items-center justify-center text-[var(--fg-muted)] hover:text-[var(--fg)] rounded-lg hover:bg-[var(--bg-elevated)]">
677
691
  <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
678
692
  <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
679
693
  </svg>
@@ -88,7 +88,9 @@ export function TaskDrawer({ taskId, onClose }: TaskDrawerProps) {
88
88
  <div className="text-sm font-semibold text-white">Task Detail</div>
89
89
  <div className="text-[10px] text-gray-500 mt-0.5">{taskId.slice(0, 16)}...</div>
90
90
  </div>
91
- <button onClick={onClose} className="text-gray-500 hover:text-white p-1.5 rounded-lg hover:bg-[#1a1a2a]">
91
+ {/* R16 of #190: same chat-panel close pattern — was ~32 px
92
+ tap target; lift to a uniform 44 x 44 hit zone. */}
93
+ <button onClick={onClose} aria-label="Close task drawer" className="inline-flex min-h-[44px] min-w-[44px] items-center justify-center text-gray-500 hover:text-white rounded-lg hover:bg-[#1a1a2a]">
92
94
  <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
93
95
  <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
94
96
  </svg>
@@ -133,7 +133,7 @@ export function UserBar() {
133
133
  setNetworkId(e.target.value);
134
134
  sessionStorage.setItem('anet_v3_auth', JSON.stringify(updated));
135
135
  }}
136
- className="bg-[#0a0a15] border border-[#2a2a4a] rounded px-2 py-1 text-xs text-white focus:outline-none"
136
+ className="bg-[#0a0a15] border border-[#2a2a4a] rounded px-2 py-1 text-base sm:text-xs text-white focus:outline-none"
137
137
  >
138
138
  {auth.networks.map(n => (
139
139
  <option key={n.network_id} value={n.network_id}>{n.network_name}</option>
@@ -160,9 +160,9 @@ export function UserBar() {
160
160
  {editing && (
161
161
  <div className="flex flex-wrap items-center gap-2 mt-2 pt-2 border-t border-[#2a2a4a]">
162
162
  <input type="text" value={editName} onChange={e => setEditName(e.target.value)} placeholder="Display name"
163
- className="bg-[#0a0a15] border border-[#2a2a4a] rounded px-2 py-1 text-xs text-white placeholder-gray-600 focus:outline-none w-32" />
163
+ className="bg-[#0a0a15] border border-[#2a2a4a] rounded px-2 py-1 text-base sm:text-xs text-white placeholder-gray-600 focus:outline-none w-32" />
164
164
  <input type="email" value={editEmail} onChange={e => setEditEmail(e.target.value)} placeholder="Email"
165
- className="bg-[#0a0a15] border border-[#2a2a4a] rounded px-2 py-1 text-xs text-white placeholder-gray-600 focus:outline-none w-40" />
165
+ className="bg-[#0a0a15] border border-[#2a2a4a] rounded px-2 py-1 text-base sm:text-xs text-white placeholder-gray-600 focus:outline-none w-40" />
166
166
  <button onClick={updateProfile} className="px-2 py-1 bg-cyan-600 hover:bg-cyan-500 text-white text-xs rounded">Save</button>
167
167
  </div>
168
168
  )}
@@ -92,7 +92,7 @@ export default function LoginPage() {
92
92
  </label>
93
93
  <input id="username" type="text" value={username} onChange={e => setUsername(e.target.value)} autoFocus
94
94
  placeholder="Enter username"
95
- className="w-full bg-[#0a0a15] border border-[#2a2a4a] rounded-lg px-4 py-3 text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:ring-1 focus:ring-cyan-500/20 focus:outline-none transition-all mb-4" />
95
+ className="w-full bg-[#0a0a15] border border-[#2a2a4a] rounded-lg px-4 py-3 text-base sm:text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:ring-1 focus:ring-cyan-500/20 focus:outline-none transition-all mb-4" />
96
96
 
97
97
  <label htmlFor="password" className="block text-xs text-gray-500 mb-2 uppercase tracking-wider">
98
98
  Password
@@ -100,13 +100,13 @@ export default function LoginPage() {
100
100
  <div className="relative">
101
101
  <input id="password" type={showPassword ? 'text' : 'password'} value={password} onChange={e => setPassword(e.target.value)}
102
102
  placeholder="Enter password"
103
- className="w-full bg-[#0a0a15] border border-[#2a2a4a] rounded-lg px-4 pr-11 py-3 text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:ring-1 focus:ring-cyan-500/20 focus:outline-none transition-all" />
103
+ className="w-full bg-[#0a0a15] border border-[#2a2a4a] rounded-lg px-4 pr-11 py-3 text-base sm:text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:ring-1 focus:ring-cyan-500/20 focus:outline-none transition-all" />
104
104
  <button
105
105
  type="button"
106
106
  onClick={() => setShowPassword(s => !s)}
107
107
  tabIndex={-1}
108
108
  aria-label={showPassword ? 'Hide password' : 'Show password'}
109
- className="absolute inset-y-0 right-0 flex items-center px-3 text-gray-600 hover:text-gray-300 transition-colors"
109
+ className="absolute inset-y-0 right-0 flex items-center justify-center min-w-[44px] px-3 text-gray-600 hover:text-gray-300 transition-colors"
110
110
  >
111
111
  {showPassword ? (
112
112
  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
package/app/logs/page.tsx CHANGED
@@ -162,11 +162,15 @@ export default function LogsPage() {
162
162
  ) : logs.length === 0 ? (
163
163
  <EmptyState variant="logs" />
164
164
  ) : (
165
- <div className="space-y-2">
165
+ /* R18 of #190 mobile polish: /logs was 28,791 px on 390 px
166
+ mobile; same dense-card pattern that R7/R8 hit on /nodes
167
+ and /tasks. Tighten card padding and inter-card gap on
168
+ mobile, sm:-gated so desktop stays comfortable. */
169
+ <div className="space-y-1 sm:space-y-2">
166
170
  {logs.map(log => {
167
171
  const userName = log.username || log.user_id;
168
172
  return (
169
- <div key={log.id} className="relative bg-[#111128] border border-[#2a2a4a] rounded-lg pl-4 pr-4 py-3 hover:border-[#3a3a5a] transition-colors overflow-hidden">
173
+ <div key={log.id} className="relative bg-[#111128] border border-[#2a2a4a] rounded-lg pl-3 pr-3 py-2 sm:pl-4 sm:pr-4 sm:py-3 hover:border-[#3a3a5a] transition-colors overflow-hidden">
170
174
  {/* 2px left rail per action (round 33) — failed logins, token
171
175
  rotations spike out of a wall of register/login rows. */}
172
176
  <span
@@ -148,12 +148,12 @@ export default function MessagesPage() {
148
148
  value={search}
149
149
  onChange={e => setSearch(e.target.value)}
150
150
  placeholder="Search from/to/content or use from:alias"
151
- className="bg-[#111128] border border-[#2a2a4a] rounded-lg px-3 py-2 text-sm text-white placeholder-gray-600 focus:border-blue-500/50 focus:outline-none w-full sm:w-72"
151
+ className="bg-[#111128] border border-[#2a2a4a] rounded-lg px-3 py-2 text-base sm:text-sm text-white placeholder-gray-600 focus:border-blue-500/50 focus:outline-none w-full sm:w-72"
152
152
  />
153
153
  <select
154
154
  value={filterType}
155
155
  onChange={e => setFilterType(e.target.value)}
156
- className="bg-[#111128] border border-[#2a2a4a] rounded-lg px-3 py-2 text-sm text-white focus:border-blue-500/50 focus:outline-none"
156
+ className="bg-[#111128] border border-[#2a2a4a] rounded-lg px-3 py-2 text-base sm:text-sm text-white focus:border-blue-500/50 focus:outline-none"
157
157
  >
158
158
  <option value="">All types</option>
159
159
  <option value="task">task</option>
@@ -260,19 +260,26 @@ export default function MessagesPage() {
260
260
 
261
261
  return (
262
262
  <div key={message.id}>
263
+ {/* R6 of #190 mobile polish: gap divider was my-4 (=16px
264
+ each side), and with multi-hour message lulls it
265
+ fired several times per session, eating ~32px each.
266
+ Halve it on mobile. */}
263
267
  {gapExceeded && (
264
- <div className="my-4 flex items-center gap-3">
268
+ <div className="my-2 sm:my-4 flex items-center gap-3">
265
269
  <div className="h-px flex-1 bg-[#2a2a4a]" />
266
270
  <div className="text-[11px] text-gray-600">{formatDividerLabel(message.created_at)}</div>
267
271
  <div className="h-px flex-1 bg-[#2a2a4a]" />
268
272
  </div>
269
273
  )}
270
274
 
271
- {/* Broadcasts span full width — no avatar gutter. */}
275
+ {/* Broadcasts span full width — no avatar gutter. R6 mobile:
276
+ tighter padding + tighter header margin + snug leading
277
+ on the content so each bubble is ~25-30% shorter at
278
+ 390px. Desktop unchanged from sm: up. */}
272
279
  {variant === 'broadcast' ? (
273
- <div className={samePrev ? 'mt-1' : 'mt-3'}>
274
- <div className="rounded-2xl border border-purple-500/20 bg-purple-500/10 px-4 py-3 shadow-sm w-full">
275
- <div className="mb-2 flex flex-wrap items-center gap-2">
280
+ <div className={samePrev ? 'mt-0.5 sm:mt-1' : 'mt-2 sm:mt-3'}>
281
+ <div className="rounded-2xl border border-purple-500/20 bg-purple-500/10 px-3 py-2 sm:px-4 sm:py-3 shadow-sm w-full">
282
+ <div className="mb-1 sm:mb-2 flex flex-wrap items-center gap-2">
276
283
  <span className={`text-xs px-2 py-0.5 rounded-md border ${TYPE_COLORS[message.type || ''] || 'bg-gray-500/10 text-gray-400 border-gray-500/20'}`}>
277
284
  {message.type || 'unknown'}
278
285
  </span>
@@ -285,24 +292,24 @@ export default function MessagesPage() {
285
292
  long unbroken runs (URLs, ASCII rules like
286
293
  `═══════════════`) wrap instead of pushing the
287
294
  chat bubble past the mobile viewport. */}
288
- <div className="whitespace-pre-wrap break-words [overflow-wrap:anywhere] text-sm leading-relaxed text-gray-200">
295
+ <div className="whitespace-pre-wrap break-words [overflow-wrap:anywhere] text-sm leading-snug sm:leading-relaxed text-gray-200">
289
296
  {renderHighlighted(message.content, search)}
290
297
  </div>
291
298
  </div>
292
299
  </div>
293
300
  ) : (
294
- <div className={`${samePrev ? 'mt-1' : 'mt-3'} flex gap-2 ${variant === 'outgoing' ? 'flex-row-reverse' : 'flex-row'}`}>
301
+ <div className={`${samePrev ? 'mt-0.5 sm:mt-1' : 'mt-2 sm:mt-3'} flex gap-2 ${variant === 'outgoing' ? 'flex-row-reverse' : 'flex-row'}`}>
295
302
  {/* Avatar gutter — fixed width keeps bubble columns aligned even on streaks */}
296
303
  <div className="w-8 shrink-0 pt-1">
297
304
  {!samePrev && <AliasAvatar alias={fromAlias} size={32} />}
298
305
  </div>
299
- <div className={`min-w-0 max-w-3xl rounded-2xl border px-4 py-3 shadow-sm ${
306
+ <div className={`min-w-0 max-w-3xl rounded-2xl border px-3 py-2 sm:px-4 sm:py-3 shadow-sm ${
300
307
  variant === 'outgoing'
301
308
  ? 'border-green-500/20 bg-green-500/10'
302
309
  : 'border-blue-500/20 bg-blue-500/10'
303
310
  }`}>
304
311
  {!samePrev && (
305
- <div className="mb-2 flex flex-wrap items-center gap-2">
312
+ <div className="mb-1 sm:mb-2 flex flex-wrap items-center gap-2">
306
313
  <span className={`text-xs px-2 py-0.5 rounded-md border ${TYPE_COLORS[message.type || ''] || 'bg-gray-500/10 text-gray-400 border-gray-500/20'}`}>
307
314
  {message.type || 'unknown'}
308
315
  </span>
@@ -320,7 +327,7 @@ export default function MessagesPage() {
320
327
  long unbroken runs (URLs, ASCII rules like
321
328
  `═══════════════`) wrap instead of pushing the
322
329
  chat bubble past the mobile viewport. */}
323
- <div className="whitespace-pre-wrap break-words [overflow-wrap:anywhere] text-sm leading-relaxed text-gray-200">
330
+ <div className="whitespace-pre-wrap break-words [overflow-wrap:anywhere] text-sm leading-snug sm:leading-relaxed text-gray-200">
324
331
  {renderHighlighted(message.content, search)}
325
332
  </div>
326
333
 
package/app/node/page.tsx CHANGED
@@ -302,12 +302,12 @@ function NodeFullPanel({ alias, session, sse, sendMsg, setSendMsg, sending, send
302
302
  onChange={e => setSendMsg(e.target.value)}
303
303
  onKeyDown={e => e.key === 'Enter' && sendTask()}
304
304
  placeholder={`Send task to ${alias}...`}
305
- className="flex-1 bg-[#0a0a15] border border-[#2a2a4a] rounded px-3 py-2 text-xs text-white placeholder-gray-600 focus:border-blue-500 focus:outline-none"
305
+ className="flex-1 bg-[#0a0a15] border border-[#2a2a4a] rounded px-3 py-2 text-base sm:text-xs text-white placeholder-gray-600 focus:border-blue-500 focus:outline-none"
306
306
  />
307
307
  <button
308
308
  onClick={sendTask}
309
309
  disabled={sending || !sendMsg.trim()}
310
- className="px-3 py-2 bg-blue-600 hover:bg-blue-500 disabled:bg-gray-700 text-white text-xs rounded transition-colors"
310
+ className="inline-flex min-h-[44px] items-center justify-center px-4 py-2 bg-blue-600 hover:bg-blue-500 disabled:bg-gray-700 text-white text-xs rounded transition-colors"
311
311
  >
312
312
  {sending ? '...' : 'Send'}
313
313
  </button>
@@ -94,12 +94,12 @@ export default function NodesPage() {
94
94
  value={search}
95
95
  onChange={e => setSearch(e.target.value)}
96
96
  placeholder="Search nodes..."
97
- className="bg-[#111128] border border-[#2a2a4a] rounded-lg px-3 py-2 text-sm text-white placeholder-gray-600 focus:border-blue-500/50 focus:outline-none w-48"
97
+ className="bg-[#111128] border border-[#2a2a4a] rounded-lg px-3 py-2 text-base sm:text-sm text-white placeholder-gray-600 focus:border-blue-500/50 focus:outline-none w-full sm:w-48"
98
98
  />
99
99
  <select
100
100
  value={filterStatus}
101
101
  onChange={e => setFilterStatus(e.target.value)}
102
- className="bg-[#111128] border border-[#2a2a4a] rounded-lg px-3 py-2 text-sm text-white focus:border-blue-500/50 focus:outline-none"
102
+ className="bg-[#111128] border border-[#2a2a4a] rounded-lg px-3 py-2 text-base sm:text-sm text-white focus:border-blue-500/50 focus:outline-none"
103
103
  >
104
104
  <option value="">All</option>
105
105
  <option value="online">Online</option>
@@ -215,7 +215,7 @@ export default function NodesPage() {
215
215
  })}
216
216
  </div>
217
217
  ) : (
218
- <div className="space-y-2">
218
+ <div className="space-y-1 sm:space-y-2">
219
219
  {/* Round 94: AGENT + SERVER merged into one `agent · server`
220
220
  cell. Round mobile-command: node row itself opens chat, so
221
221
  the old Chat / Send Task action column is gone. */}
@@ -237,7 +237,7 @@ export default function NodesPage() {
237
237
  tabIndex={0}
238
238
  onClick={() => setChatAlias(s.alias)}
239
239
  onKeyDown={e => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); setChatAlias(s.alias); } }}
240
- className={`rounded-lg border border-[#2a2a4a] bg-[#111128] px-4 py-3 transition-colors hover:border-cyan-500/40 cursor-pointer ${!s.online ? 'opacity-50' : ''}`}
240
+ className={`rounded-lg border border-[#2a2a4a] bg-[#111128] px-3 py-2 sm:px-4 sm:py-3 transition-colors hover:border-cyan-500/40 cursor-pointer ${!s.online ? 'opacity-50' : ''}`}
241
241
  >
242
242
  <div className="hidden sm:grid sm:grid-cols-10 gap-2 items-center">
243
243
  <div className="col-span-1">
@@ -261,7 +261,14 @@ export default function NodesPage() {
261
261
  <div className="col-span-4 truncate text-xs text-gray-500" title={s.task || ''}>{s.task || '--'}</div>
262
262
  <div className="col-span-1 text-xs text-gray-500">{timeAgo(s.last_seen_at || s.updated_at)}</div>
263
263
  </div>
264
- <div className="sm:hidden space-y-2">
264
+ {/* R7 of #190: mobile node row was ~340px tall × ~150
265
+ rows = the 51k page. Wins this round, in priority
266
+ order: (1) drop the per-row "Tap anywhere to chat"
267
+ hint — useful once, redundant 149 times; the cyan
268
+ border on hover/focus still teaches it. (2) tighten
269
+ space-y-2 → space-y-1 so the avatar/task gap is
270
+ 4px tighter on every row. */}
271
+ <div className="sm:hidden space-y-1">
265
272
  <div className="flex items-center gap-2.5">
266
273
  <div className="relative shrink-0">
267
274
  <AliasAvatar alias={s.alias} size={28} />
@@ -279,7 +286,6 @@ export default function NodesPage() {
279
286
  </span>
280
287
  </div>
281
288
  {s.task && <div className="truncate text-xs text-gray-500">{s.task}</div>}
282
- <div className="text-[10px] text-cyan-300/70">Tap anywhere to chat</div>
283
289
  </div>
284
290
  </div>
285
291
  );
@@ -196,7 +196,7 @@ export default function ServerLogsPage() {
196
196
  value={search}
197
197
  onChange={e => setSearch(e.target.value)}
198
198
  placeholder="搜索关键字 (alias / task_id / error message)"
199
- className="flex-1 min-w-[140px] basis-full sm:basis-0 px-3 py-1.5 text-xs bg-[#11111c] border border-[#2a2a4a] rounded text-gray-200 focus:outline-none focus:border-cyan-500/40"
199
+ className="flex-1 min-w-[140px] basis-full sm:basis-0 px-3 py-1.5 text-base sm:text-xs bg-[#11111c] border border-[#2a2a4a] rounded text-gray-200 focus:outline-none focus:border-cyan-500/40"
200
200
  />
201
201
  <span className="text-[10px] text-gray-600">
202
202
  {filtered.length} / {logs.length}
@@ -252,8 +252,15 @@ export default function ServerLogsPage() {
252
252
  className="absolute left-0 top-0 bottom-0 w-0.5"
253
253
  style={{ backgroundColor: LEVEL_STRIPE[l.level] }}
254
254
  />
255
- <span className="text-gray-600 shrink-0 w-[100px] text-[10px] tabular-nums">{shortTime(l.ts)}</span>
256
- <span className={`shrink-0 px-1.5 rounded border text-[9px] uppercase ${LEVEL_BADGE[l.level]}`}>
255
+ {/* R4 of #190 mobile polish: the 100px timestamp column +
256
+ 32px LEVEL badge ate ~45% of a 375px row, squeezing log
257
+ content into a 3-line wrap. Drop the ts column to 60px
258
+ on mobile (HH:MM:SS still fits at 9px), restore 100px
259
+ at sm. The 2px left rail (LEVEL_STRIPE) already
260
+ encodes level visually for warn/error, so the LOG badge
261
+ is redundant on mobile — hide it below sm. */}
262
+ <span className="text-gray-600 shrink-0 w-[60px] sm:w-[100px] text-[9px] sm:text-[10px] tabular-nums">{shortTime(l.ts)}</span>
263
+ <span className={`hidden sm:inline shrink-0 px-1.5 rounded border text-[9px] uppercase ${LEVEL_BADGE[l.level]}`}>
257
264
  {l.level}
258
265
  </span>
259
266
  {/* Round 85: CommHub stamps each log line with a `[HH:MM:SS]`
@@ -111,9 +111,9 @@ export default function NetworksPage() {
111
111
  <h2 className="text-sm font-semibold text-gray-300 mb-3">Create Network</h2>
112
112
  <div className="space-y-3">
113
113
  <input type="text" value={newName} onChange={e => setNewName(e.target.value)} placeholder="Network name"
114
- className="w-full bg-[#0a0a15] border border-[#2a2a4a] rounded-lg px-3 py-2 text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:outline-none" />
114
+ className="w-full bg-[#0a0a15] border border-[#2a2a4a] rounded-lg px-3 py-2 text-base sm:text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:outline-none" />
115
115
  <input type="text" value={newDesc} onChange={e => setNewDesc(e.target.value)} placeholder="Description (optional)"
116
- className="w-full bg-[#0a0a15] border border-[#2a2a4a] rounded-lg px-3 py-2 text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:outline-none" />
116
+ className="w-full bg-[#0a0a15] border border-[#2a2a4a] rounded-lg px-3 py-2 text-base sm:text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:outline-none" />
117
117
  <button onClick={createNetwork} disabled={!newName.trim()}
118
118
  className="px-4 py-2 bg-green-600 hover:bg-green-500 disabled:bg-gray-800 text-white text-sm rounded-lg transition-colors">
119
119
  Create
@@ -199,7 +199,7 @@ export default function NetworksPage() {
199
199
  <div className="text-xs text-gray-500 mb-2">Invite</div>
200
200
  <div className="flex gap-2">
201
201
  <select value={inviteRole} onChange={e => setInviteRole(e.target.value)}
202
- className="bg-[#0a0a15] border border-[#2a2a4a] rounded px-2 py-1 text-xs text-white focus:outline-none">
202
+ className="bg-[#0a0a15] border border-[#2a2a4a] rounded px-2 py-1 text-base sm:text-xs text-white focus:outline-none">
203
203
  <option value="member">member</option>
204
204
  <option value="admin">admin</option>
205
205
  <option value="viewer">viewer</option>
@@ -23,15 +23,18 @@ export default function SettingsPage() {
23
23
 
24
24
  {/* Section anchor nav (round 28) — jump to a group instead of scrolling
25
25
  through the whole page. Mirrors the section headers below; pages
26
- with 7+ cards benefit from a visible table-of-contents. */}
27
- <nav className="mb-8 flex flex-wrap gap-1 text-xs">
26
+ with 7+ cards benefit from a visible table-of-contents.
27
+ R3 of #190 mobile polish: matches the /admin chip treatment
28
+ (preview.3) — 44px tap-target + visible border so the chips read
29
+ as tappable rather than as inert headings on 375–390px. */}
30
+ <nav className="mb-8 flex flex-wrap gap-2 text-xs">
28
31
  {[
29
32
  { href: '#connection', label: 'Connection' },
30
33
  { href: '#account', label: 'Account' },
31
34
  { href: '#resources', label: 'Resources' },
32
35
  ].map(a => (
33
36
  <a key={a.href} href={a.href}
34
- className="rounded-md px-2.5 py-1 text-gray-500 hover:text-cyan-300 hover:bg-cyan-500/10 transition-colors">
37
+ className="inline-flex min-h-[44px] items-center rounded-md border border-[#2a2a4a] bg-[#0a0a15]/60 px-3 py-2 text-gray-400 hover:border-cyan-500/50 hover:text-cyan-300 hover:bg-cyan-500/10 transition-colors">
35
38
  {a.label}
36
39
  </a>
37
40
  ))}
@@ -209,9 +212,9 @@ export default function SettingsPage() {
209
212
  <h2 className="text-sm font-semibold text-gray-300 mb-4">Change Password</h2>
210
213
  <div className="space-y-3">
211
214
  <input type="password" value={oldPwd} onChange={e => setOldPwd(e.target.value)} placeholder="Current password"
212
- className="w-full bg-[#0a0a15] border border-[#2a2a4a] rounded-lg px-3 py-2 text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:outline-none" />
215
+ className="w-full bg-[#0a0a15] border border-[#2a2a4a] rounded-lg px-3 py-2 text-base sm:text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:outline-none" />
213
216
  <input type="password" value={newPwd} onChange={e => setNewPwd(e.target.value)} placeholder="New password"
214
- className="w-full bg-[#0a0a15] border border-[#2a2a4a] rounded-lg px-3 py-2 text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:outline-none" />
217
+ className="w-full bg-[#0a0a15] border border-[#2a2a4a] rounded-lg px-3 py-2 text-base sm:text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:outline-none" />
215
218
  <button onClick={async () => {
216
219
  if (!oldPwd || !newPwd) return;
217
220
  const saved = sessionStorage.getItem('anet_v3_auth');
@@ -80,7 +80,7 @@ export default function TokensPage() {
80
80
  <div className="flex gap-2">
81
81
  <input type="text" value={newName} onChange={e => setNewName(e.target.value)}
82
82
  placeholder="Token name (e.g. my-cli)"
83
- className="flex-1 bg-[#0a0a15] border border-[#2a2a4a] rounded-lg px-3 py-2 text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:outline-none" />
83
+ className="flex-1 bg-[#0a0a15] border border-[#2a2a4a] rounded-lg px-3 py-2 text-base sm:text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:outline-none" />
84
84
  <button onClick={createToken} disabled={!newName.trim()}
85
85
  className="px-4 py-2 bg-cyan-600 hover:bg-cyan-500 disabled:bg-gray-800 text-white text-sm rounded-lg transition-colors">
86
86
  Create
@@ -180,7 +180,7 @@ function TasksContent() {
180
180
  value={filterFrom}
181
181
  onChange={e => setFilterFrom(e.target.value)}
182
182
  placeholder="any node"
183
- className="w-28 bg-transparent text-sm text-white placeholder-gray-700 focus:outline-none"
183
+ className="w-28 bg-transparent text-base sm:text-sm text-white placeholder-gray-700 focus:outline-none"
184
184
  />
185
185
  </div>
186
186
  <div className="flex items-center gap-1.5 rounded-lg border border-[#2a2a4a] bg-[#111128] px-2.5 py-1.5 focus-within:border-blue-500/40">
@@ -190,7 +190,7 @@ function TasksContent() {
190
190
  value={filterTo}
191
191
  onChange={e => setFilterTo(e.target.value)}
192
192
  placeholder="any node"
193
- className="w-28 bg-transparent text-sm text-white placeholder-gray-700 focus:outline-none"
193
+ className="w-28 bg-transparent text-base sm:text-sm text-white placeholder-gray-700 focus:outline-none"
194
194
  />
195
195
  </div>
196
196
  {(filterStatus || filterFrom || filterTo) && (
@@ -248,7 +248,7 @@ function TasksContent() {
248
248
  : 'Tasks will appear here when agents send them via CommHub.'}
249
249
  />
250
250
  ) : (
251
- <div className="space-y-2">
251
+ <div className="space-y-1 sm:space-y-2">
252
252
  {/* Table header */}
253
253
  <div className="hidden sm:grid sm:grid-cols-12 gap-2 px-4 py-2 text-xs text-gray-600 uppercase">
254
254
  <div className="col-span-1">Status</div>
@@ -264,7 +264,7 @@ function TasksContent() {
264
264
  return (
265
265
  <div
266
266
  key={t.task_id}
267
- className={`anet-task-row group bg-[#111128] border rounded-lg px-4 py-3 transition-all duration-200 cursor-pointer ${
267
+ className={`anet-task-row group bg-[#111128] border rounded-lg px-3 py-2 sm:px-4 sm:py-3 transition-all duration-200 cursor-pointer ${
268
268
  isOpen
269
269
  ? 'border-[#3a3a5a] shadow-lg shadow-black/20'
270
270
  : 'border-[#2a2a4a] hover:border-[#3a3a5a] hover:bg-[#15152e]'
@@ -311,8 +311,12 @@ function TasksContent() {
311
311
  </div>
312
312
  </div>
313
313
 
314
- {/* Mobile layout */}
315
- <div className="sm:hidden space-y-2">
314
+ {/* Mobile layout — R8 of #190 mobile polish: 4-row stack
315
+ 3-row by inlining timeAgo onto the same row as the
316
+ from→to alias header (it's already 4 small atoms, has
317
+ room), plus space-y-2 → space-y-1 to trim ~4px per
318
+ row × 200 tasks. */}
319
+ <div className="sm:hidden space-y-1">
316
320
  <div className="flex items-center justify-between">
317
321
  <span className={statusBadge(t.status)}>{t.status}</span>
318
322
  <div className="flex items-center gap-2">
@@ -328,13 +332,13 @@ function TasksContent() {
328
332
  </div>
329
333
  <div className="flex items-center gap-1.5 text-xs text-gray-300 min-w-0">
330
334
  {t.from_name && <AliasAvatar alias={t.from_name} size={16} />}
331
- <span className="truncate max-w-[40%]">{t.from_name || '--'}</span>
335
+ <span className="truncate max-w-[35%]">{t.from_name || '--'}</span>
332
336
  <span className="text-gray-600">&rarr;</span>
333
337
  {t.to_name && <AliasAvatar alias={t.to_name} size={16} />}
334
- <span className="truncate max-w-[40%]">{t.to_name || '--'}</span>
338
+ <span className="truncate max-w-[35%]">{t.to_name || '--'}</span>
339
+ <span className="ml-auto shrink-0 text-[10px] text-gray-600">{timeAgo(t.created_at)}</span>
335
340
  </div>
336
341
  <div className="text-xs text-gray-400 line-clamp-1" title={t.content}>{previewContent(t.content)}</div>
337
- <div className="text-xs text-gray-600">{timeAgo(t.created_at)}</div>
338
342
  </div>
339
343
 
340
344
  {/* Expanded detail — always mounted; grid-rows 0fr↔1fr trick gives
package/bin/start.js CHANGED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sleep2agi/agent-network-dashboard",
3
- "version": "0.5.7-preview.2",
3
+ "version": "0.5.7-preview.21",
4
4
  "description": "Agent Network Dashboard — Web UI for managing AI Agent networks",
5
5
  "main": "apps/desktop/electron/main.cjs",
6
6
  "scripts": {