@jlongo78/agent-spaces 0.9.6 → 0.9.8

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 (917) hide show
  1. package/.next/standalone/.claude/settings.local.json +68 -0
  2. package/.next/standalone/.claude/spaces-env.json +1 -0
  3. package/.next/standalone/.next/BUILD_ID +1 -1
  4. package/.next/standalone/.next/app-path-routes-manifest.json +1 -0
  5. package/.next/standalone/.next/build-manifest.json +2 -2
  6. package/.next/standalone/.next/prerender-manifest.json +3 -3
  7. package/.next/standalone/.next/required-server-files.json +19 -19
  8. package/.next/standalone/.next/routes-manifest.json +6 -0
  9. package/.next/standalone/.next/server/app/(desktop)/admin/analytics/page_client-reference-manifest.js +1 -1
  10. package/.next/standalone/.next/server/app/(desktop)/admin/users/page_client-reference-manifest.js +1 -1
  11. package/.next/standalone/.next/server/app/(desktop)/analytics/page_client-reference-manifest.js +1 -1
  12. package/.next/standalone/.next/server/app/(desktop)/cortex/page_client-reference-manifest.js +1 -1
  13. package/.next/standalone/.next/server/app/(desktop)/network/page_client-reference-manifest.js +1 -1
  14. package/.next/standalone/.next/server/app/(desktop)/page_client-reference-manifest.js +1 -1
  15. package/.next/standalone/.next/server/app/(desktop)/projects/page_client-reference-manifest.js +1 -1
  16. package/.next/standalone/.next/server/app/(desktop)/sessions/[id]/page.js.nft.json +1 -1
  17. package/.next/standalone/.next/server/app/(desktop)/sessions/[id]/page_client-reference-manifest.js +1 -1
  18. package/.next/standalone/.next/server/app/(desktop)/sessions/page_client-reference-manifest.js +1 -1
  19. package/.next/standalone/.next/server/app/(desktop)/settings/page_client-reference-manifest.js +1 -1
  20. package/.next/standalone/.next/server/app/(desktop)/terminal/page.js.nft.json +1 -1
  21. package/.next/standalone/.next/server/app/(desktop)/terminal/page_client-reference-manifest.js +1 -1
  22. package/.next/standalone/.next/server/app/(desktop)/terminal/pane/[id]/page_client-reference-manifest.js +1 -1
  23. package/.next/standalone/.next/server/app/(desktop)/terminal/remote/[nodeId]/[workspaceId]/page_client-reference-manifest.js +1 -1
  24. package/.next/standalone/.next/server/app/(desktop)/workspaces/page_client-reference-manifest.js +1 -1
  25. package/.next/standalone/.next/server/app/_global-error.html +2 -2
  26. package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
  27. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  28. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  29. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  30. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  31. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  32. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  33. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  34. package/.next/standalone/.next/server/app/_not-found.rsc +2 -2
  35. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  36. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  37. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  38. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  39. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  40. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  41. package/.next/standalone/.next/server/app/admin/analytics.html +1 -1
  42. package/.next/standalone/.next/server/app/admin/analytics.rsc +2 -2
  43. package/.next/standalone/.next/server/app/admin/analytics.segments/!KGRlc2t0b3Ap/admin/analytics/__PAGE__.segment.rsc +1 -1
  44. package/.next/standalone/.next/server/app/admin/analytics.segments/!KGRlc2t0b3Ap/admin/analytics.segment.rsc +1 -1
  45. package/.next/standalone/.next/server/app/admin/analytics.segments/!KGRlc2t0b3Ap/admin.segment.rsc +1 -1
  46. package/.next/standalone/.next/server/app/admin/analytics.segments/!KGRlc2t0b3Ap.segment.rsc +1 -1
  47. package/.next/standalone/.next/server/app/admin/analytics.segments/_full.segment.rsc +2 -2
  48. package/.next/standalone/.next/server/app/admin/analytics.segments/_head.segment.rsc +1 -1
  49. package/.next/standalone/.next/server/app/admin/analytics.segments/_index.segment.rsc +2 -2
  50. package/.next/standalone/.next/server/app/admin/analytics.segments/_tree.segment.rsc +2 -2
  51. package/.next/standalone/.next/server/app/admin/users.html +1 -1
  52. package/.next/standalone/.next/server/app/admin/users.rsc +2 -2
  53. package/.next/standalone/.next/server/app/admin/users.segments/!KGRlc2t0b3Ap/admin/users/__PAGE__.segment.rsc +1 -1
  54. package/.next/standalone/.next/server/app/admin/users.segments/!KGRlc2t0b3Ap/admin/users.segment.rsc +1 -1
  55. package/.next/standalone/.next/server/app/admin/users.segments/!KGRlc2t0b3Ap/admin.segment.rsc +1 -1
  56. package/.next/standalone/.next/server/app/admin/users.segments/!KGRlc2t0b3Ap.segment.rsc +1 -1
  57. package/.next/standalone/.next/server/app/admin/users.segments/_full.segment.rsc +2 -2
  58. package/.next/standalone/.next/server/app/admin/users.segments/_head.segment.rsc +1 -1
  59. package/.next/standalone/.next/server/app/admin/users.segments/_index.segment.rsc +2 -2
  60. package/.next/standalone/.next/server/app/admin/users.segments/_tree.segment.rsc +2 -2
  61. package/.next/standalone/.next/server/app/analytics.html +1 -1
  62. package/.next/standalone/.next/server/app/analytics.rsc +2 -2
  63. package/.next/standalone/.next/server/app/analytics.segments/!KGRlc2t0b3Ap/analytics/__PAGE__.segment.rsc +1 -1
  64. package/.next/standalone/.next/server/app/analytics.segments/!KGRlc2t0b3Ap/analytics.segment.rsc +1 -1
  65. package/.next/standalone/.next/server/app/analytics.segments/!KGRlc2t0b3Ap.segment.rsc +1 -1
  66. package/.next/standalone/.next/server/app/analytics.segments/_full.segment.rsc +2 -2
  67. package/.next/standalone/.next/server/app/analytics.segments/_head.segment.rsc +1 -1
  68. package/.next/standalone/.next/server/app/analytics.segments/_index.segment.rsc +2 -2
  69. package/.next/standalone/.next/server/app/analytics.segments/_tree.segment.rsc +2 -2
  70. package/.next/standalone/.next/server/app/api/analytics/overview/route.js +1 -1
  71. package/.next/standalone/.next/server/app/api/analytics/overview/route.js.nft.json +1 -1
  72. package/.next/standalone/.next/server/app/api/benchmark/lobes/route.js +1 -1
  73. package/.next/standalone/.next/server/app/api/benchmark/lobes/route.js.nft.json +1 -1
  74. package/.next/standalone/.next/server/app/api/benchmark/run/route.js +1 -1
  75. package/.next/standalone/.next/server/app/api/benchmark/run/route.js.nft.json +1 -1
  76. package/.next/standalone/.next/server/app/api/benchmark/runs/[id]/route.js +1 -1
  77. package/.next/standalone/.next/server/app/api/benchmark/runs/[id]/route.js.nft.json +1 -1
  78. package/.next/standalone/.next/server/app/api/benchmark/runs/route.js +1 -1
  79. package/.next/standalone/.next/server/app/api/benchmark/runs/route.js.nft.json +1 -1
  80. package/.next/standalone/.next/server/app/api/benchmark/status/route.js +1 -1
  81. package/.next/standalone/.next/server/app/api/benchmark/status/route.js.nft.json +1 -1
  82. package/.next/standalone/.next/server/app/api/bulk/route.js +1 -1
  83. package/.next/standalone/.next/server/app/api/bulk/route.js.nft.json +1 -1
  84. package/.next/standalone/.next/server/app/api/config/route.js +1 -1
  85. package/.next/standalone/.next/server/app/api/config/route.js.nft.json +1 -1
  86. package/.next/standalone/.next/server/app/api/cortex/context/route.js +1 -1
  87. package/.next/standalone/.next/server/app/api/cortex/context/route.js.nft.json +1 -1
  88. package/.next/standalone/.next/server/app/api/cortex/curation/assess/route.js +1 -1
  89. package/.next/standalone/.next/server/app/api/cortex/curation/assess/route.js.nft.json +1 -1
  90. package/.next/standalone/.next/server/app/api/cortex/curation/publish/route.js +1 -1
  91. package/.next/standalone/.next/server/app/api/cortex/curation/publish/route.js.nft.json +1 -1
  92. package/.next/standalone/.next/server/app/api/cortex/curation/refine/route.js +1 -1
  93. package/.next/standalone/.next/server/app/api/cortex/curation/refine/route.js.nft.json +1 -1
  94. package/.next/standalone/.next/server/app/api/cortex/curation/review/route.js +1 -1
  95. package/.next/standalone/.next/server/app/api/cortex/curation/review/route.js.nft.json +1 -1
  96. package/.next/standalone/.next/server/app/api/cortex/curation/seed/route.js +1 -1
  97. package/.next/standalone/.next/server/app/api/cortex/curation/seed/route.js.nft.json +1 -1
  98. package/.next/standalone/.next/server/app/api/cortex/export/route.js +1 -1
  99. package/.next/standalone/.next/server/app/api/cortex/export/route.js.nft.json +1 -1
  100. package/.next/standalone/.next/server/app/api/cortex/federation/pending/route.js +1 -1
  101. package/.next/standalone/.next/server/app/api/cortex/federation/pending/route.js.nft.json +1 -1
  102. package/.next/standalone/.next/server/app/api/cortex/federation/resolve/route.js +1 -1
  103. package/.next/standalone/.next/server/app/api/cortex/federation/resolve/route.js.nft.json +1 -1
  104. package/.next/standalone/.next/server/app/api/cortex/federation/search/route.js +1 -1
  105. package/.next/standalone/.next/server/app/api/cortex/federation/search/route.js.nft.json +1 -1
  106. package/.next/standalone/.next/server/app/api/cortex/federation/teach/route.js +1 -1
  107. package/.next/standalone/.next/server/app/api/cortex/federation/teach/route.js.nft.json +1 -1
  108. package/.next/standalone/.next/server/app/api/cortex/graph/edges/route.js +1 -1
  109. package/.next/standalone/.next/server/app/api/cortex/graph/edges/route.js.nft.json +1 -1
  110. package/.next/standalone/.next/server/app/api/cortex/graph/entities/[id]/route.js +1 -1
  111. package/.next/standalone/.next/server/app/api/cortex/graph/entities/[id]/route.js.nft.json +1 -1
  112. package/.next/standalone/.next/server/app/api/cortex/graph/entities/route.js +1 -1
  113. package/.next/standalone/.next/server/app/api/cortex/graph/entities/route.js.nft.json +1 -1
  114. package/.next/standalone/.next/server/app/api/cortex/graph/populate/route.js +1 -1
  115. package/.next/standalone/.next/server/app/api/cortex/graph/populate/route.js.nft.json +1 -1
  116. package/.next/standalone/.next/server/app/api/cortex/import/route.js +1 -1
  117. package/.next/standalone/.next/server/app/api/cortex/import/route.js.nft.json +1 -1
  118. package/.next/standalone/.next/server/app/api/cortex/import/status/route.js +1 -1
  119. package/.next/standalone/.next/server/app/api/cortex/import/status/route.js.nft.json +1 -1
  120. package/.next/standalone/.next/server/app/api/cortex/ingest/bootstrap/route.js +1 -1
  121. package/.next/standalone/.next/server/app/api/cortex/ingest/bootstrap/route.js.nft.json +1 -1
  122. package/.next/standalone/.next/server/app/api/cortex/ingest/status/route.js +1 -1
  123. package/.next/standalone/.next/server/app/api/cortex/ingest/status/route.js.nft.json +1 -1
  124. package/.next/standalone/.next/server/app/api/cortex/knowledge/[id]/route.js +1 -1
  125. package/.next/standalone/.next/server/app/api/cortex/knowledge/[id]/route.js.nft.json +1 -1
  126. package/.next/standalone/.next/server/app/api/cortex/knowledge/route.js +1 -1
  127. package/.next/standalone/.next/server/app/api/cortex/knowledge/route.js.nft.json +1 -1
  128. package/.next/standalone/.next/server/app/api/cortex/lobes/[id]/route.js +1 -1
  129. package/.next/standalone/.next/server/app/api/cortex/lobes/[id]/route.js.nft.json +1 -1
  130. package/.next/standalone/.next/server/app/api/cortex/lobes/route.js +1 -1
  131. package/.next/standalone/.next/server/app/api/cortex/lobes/route.js.nft.json +1 -1
  132. package/.next/standalone/.next/server/app/api/cortex/lobes/share/route.js +1 -1
  133. package/.next/standalone/.next/server/app/api/cortex/lobes/share/route.js.nft.json +1 -1
  134. package/.next/standalone/.next/server/app/api/cortex/marketplace/browse/route.js +1 -1
  135. package/.next/standalone/.next/server/app/api/cortex/marketplace/browse/route.js.nft.json +1 -1
  136. package/.next/standalone/.next/server/app/api/cortex/marketplace/preview/route.js +1 -1
  137. package/.next/standalone/.next/server/app/api/cortex/marketplace/preview/route.js.nft.json +1 -1
  138. package/.next/standalone/.next/server/app/api/cortex/mcp/call/route.js +1 -1
  139. package/.next/standalone/.next/server/app/api/cortex/mcp/call/route.js.nft.json +1 -1
  140. package/.next/standalone/.next/server/app/api/cortex/mcp/tools/route.js +1 -1
  141. package/.next/standalone/.next/server/app/api/cortex/mcp/tools/route.js.nft.json +1 -1
  142. package/.next/standalone/.next/server/app/api/cortex/search/route.js +1 -1
  143. package/.next/standalone/.next/server/app/api/cortex/search/route.js.nft.json +1 -1
  144. package/.next/standalone/.next/server/app/api/cortex/settings/route.js +1 -1
  145. package/.next/standalone/.next/server/app/api/cortex/settings/route.js.nft.json +1 -1
  146. package/.next/standalone/.next/server/app/api/cortex/status/route.js +1 -1
  147. package/.next/standalone/.next/server/app/api/cortex/status/route.js.nft.json +1 -1
  148. package/.next/standalone/.next/server/app/api/cortex/timeline/route.js +1 -1
  149. package/.next/standalone/.next/server/app/api/cortex/timeline/route.js.nft.json +1 -1
  150. package/.next/standalone/.next/server/app/api/cortex/usage/route.js +1 -1
  151. package/.next/standalone/.next/server/app/api/cortex/usage/route.js.nft.json +1 -1
  152. package/.next/standalone/.next/server/app/api/cortex/workspace/[id]/context/route.js +1 -1
  153. package/.next/standalone/.next/server/app/api/cortex/workspace/[id]/context/route.js.nft.json +1 -1
  154. package/.next/standalone/.next/server/app/api/events/route.js +1 -1
  155. package/.next/standalone/.next/server/app/api/events/route.js.nft.json +1 -1
  156. package/.next/standalone/.next/server/app/api/files/route.js +1 -1
  157. package/.next/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  158. package/.next/standalone/.next/server/app/api/folders/route.js +1 -1
  159. package/.next/standalone/.next/server/app/api/folders/route.js.nft.json +1 -1
  160. package/.next/standalone/.next/server/app/api/network/handshake/route.js +1 -1
  161. package/.next/standalone/.next/server/app/api/network/handshake/route.js.nft.json +1 -1
  162. package/.next/standalone/.next/server/app/api/network/panes/[id]/route.js +1 -1
  163. package/.next/standalone/.next/server/app/api/network/panes/[id]/route.js.nft.json +1 -1
  164. package/.next/standalone/.next/server/app/api/network/panes/route.js +1 -1
  165. package/.next/standalone/.next/server/app/api/network/panes/route.js.nft.json +1 -1
  166. package/.next/standalone/.next/server/app/api/network/projects/route.js +1 -1
  167. package/.next/standalone/.next/server/app/api/network/projects/route.js.nft.json +1 -1
  168. package/.next/standalone/.next/server/app/api/network/search/route.js +1 -1
  169. package/.next/standalone/.next/server/app/api/network/search/route.js.nft.json +1 -1
  170. package/.next/standalone/.next/server/app/api/network/sessions/[id]/messages/route.js +1 -1
  171. package/.next/standalone/.next/server/app/api/network/sessions/[id]/messages/route.js.nft.json +1 -1
  172. package/.next/standalone/.next/server/app/api/network/sessions/[id]/route.js +1 -1
  173. package/.next/standalone/.next/server/app/api/network/sessions/[id]/route.js.nft.json +1 -1
  174. package/.next/standalone/.next/server/app/api/network/sessions/route.js +1 -1
  175. package/.next/standalone/.next/server/app/api/network/sessions/route.js.nft.json +1 -1
  176. package/.next/standalone/.next/server/app/api/network/workspaces/[id]/route.js +1 -1
  177. package/.next/standalone/.next/server/app/api/network/workspaces/[id]/route.js.nft.json +1 -1
  178. package/.next/standalone/.next/server/app/api/network/workspaces/route.js +1 -1
  179. package/.next/standalone/.next/server/app/api/network/workspaces/route.js.nft.json +1 -1
  180. package/.next/standalone/.next/server/app/api/panes/[id]/diff/route.js +1 -1
  181. package/.next/standalone/.next/server/app/api/panes/[id]/diff/route.js.nft.json +1 -1
  182. package/.next/standalone/.next/server/app/api/panes/[id]/route.js +1 -1
  183. package/.next/standalone/.next/server/app/api/panes/[id]/route.js.nft.json +1 -1
  184. package/.next/standalone/.next/server/app/api/panes/route.js +1 -1
  185. package/.next/standalone/.next/server/app/api/panes/route.js.nft.json +1 -1
  186. package/.next/standalone/.next/server/app/api/projects/route.js +1 -1
  187. package/.next/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  188. package/.next/standalone/.next/server/app/api/proxy/models/[modelId]/[...path]/route.js +1 -1
  189. package/.next/standalone/.next/server/app/api/proxy/models/[modelId]/[...path]/route.js.nft.json +1 -1
  190. package/.next/standalone/.next/server/app/api/proxy/models/[modelId]/status/route.js +1 -1
  191. package/.next/standalone/.next/server/app/api/proxy/models/[modelId]/status/route.js.nft.json +1 -1
  192. package/.next/standalone/.next/server/app/api/search/route.js +2 -2
  193. package/.next/standalone/.next/server/app/api/search/route.js.nft.json +1 -1
  194. package/.next/standalone/.next/server/app/api/sessions/[id]/chat/route.js +1 -1
  195. package/.next/standalone/.next/server/app/api/sessions/[id]/chat/route.js.nft.json +1 -1
  196. package/.next/standalone/.next/server/app/api/sessions/[id]/messages/route.js +1 -1
  197. package/.next/standalone/.next/server/app/api/sessions/[id]/messages/route.js.nft.json +1 -1
  198. package/.next/standalone/.next/server/app/api/sessions/[id]/route.js +1 -1
  199. package/.next/standalone/.next/server/app/api/sessions/[id]/route.js.nft.json +1 -1
  200. package/.next/standalone/.next/server/app/api/sessions/route.js +2 -2
  201. package/.next/standalone/.next/server/app/api/sessions/route.js.nft.json +1 -1
  202. package/.next/standalone/.next/server/app/api/sync/route.js +1 -1
  203. package/.next/standalone/.next/server/app/api/sync/route.js.nft.json +1 -1
  204. package/.next/standalone/.next/server/app/api/tags/route.js +1 -1
  205. package/.next/standalone/.next/server/app/api/tags/route.js.nft.json +1 -1
  206. package/.next/standalone/.next/server/app/api/tier/route.js +1 -1
  207. package/.next/standalone/.next/server/app/api/tier/route.js.nft.json +1 -1
  208. package/.next/standalone/.next/server/app/api/whisper/config/route.js +1 -1
  209. package/.next/standalone/.next/server/app/api/whisper/config/route.js.nft.json +1 -1
  210. package/.next/standalone/.next/server/app/api/whisper/route.js.nft.json +1 -1
  211. package/.next/standalone/.next/server/app/api/wizard/chart/route/app-paths-manifest.json +3 -0
  212. package/.next/standalone/.next/server/app/api/wizard/chart/route/build-manifest.json +11 -0
  213. package/.next/standalone/.next/server/app/api/wizard/chart/route/server-reference-manifest.json +4 -0
  214. package/.next/standalone/.next/server/app/api/wizard/chart/route.js +7 -0
  215. package/.next/standalone/.next/server/app/api/wizard/chart/route.js.map +5 -0
  216. package/.next/standalone/.next/server/app/api/wizard/chart/route.js.nft.json +1 -0
  217. package/.next/standalone/.next/server/app/api/wizard/chart/route_client-reference-manifest.js +2 -0
  218. package/.next/standalone/.next/server/app/api/wizard/chat/route.js +1 -1
  219. package/.next/standalone/.next/server/app/api/wizard/chat/route.js.nft.json +1 -1
  220. package/.next/standalone/.next/server/app/api/workspaces/[id]/context/[key]/route.js +1 -1
  221. package/.next/standalone/.next/server/app/api/workspaces/[id]/context/[key]/route.js.nft.json +1 -1
  222. package/.next/standalone/.next/server/app/api/workspaces/[id]/context/route.js +1 -1
  223. package/.next/standalone/.next/server/app/api/workspaces/[id]/context/route.js.nft.json +1 -1
  224. package/.next/standalone/.next/server/app/api/workspaces/[id]/messages/[msgId]/route.js +1 -1
  225. package/.next/standalone/.next/server/app/api/workspaces/[id]/messages/[msgId]/route.js.nft.json +1 -1
  226. package/.next/standalone/.next/server/app/api/workspaces/[id]/messages/route.js +1 -1
  227. package/.next/standalone/.next/server/app/api/workspaces/[id]/messages/route.js.nft.json +1 -1
  228. package/.next/standalone/.next/server/app/api/workspaces/[id]/route.js +1 -1
  229. package/.next/standalone/.next/server/app/api/workspaces/[id]/route.js.nft.json +1 -1
  230. package/.next/standalone/.next/server/app/api/workspaces/[id]/sessions/route.js +1 -1
  231. package/.next/standalone/.next/server/app/api/workspaces/[id]/sessions/route.js.nft.json +1 -1
  232. package/.next/standalone/.next/server/app/api/workspaces/route.js +2 -2
  233. package/.next/standalone/.next/server/app/api/workspaces/route.js.nft.json +1 -1
  234. package/.next/standalone/.next/server/app/cortex.html +1 -1
  235. package/.next/standalone/.next/server/app/cortex.rsc +3 -3
  236. package/.next/standalone/.next/server/app/cortex.segments/!KGRlc2t0b3Ap/cortex/__PAGE__.segment.rsc +2 -2
  237. package/.next/standalone/.next/server/app/cortex.segments/!KGRlc2t0b3Ap/cortex.segment.rsc +1 -1
  238. package/.next/standalone/.next/server/app/cortex.segments/!KGRlc2t0b3Ap.segment.rsc +1 -1
  239. package/.next/standalone/.next/server/app/cortex.segments/_full.segment.rsc +3 -3
  240. package/.next/standalone/.next/server/app/cortex.segments/_head.segment.rsc +1 -1
  241. package/.next/standalone/.next/server/app/cortex.segments/_index.segment.rsc +2 -2
  242. package/.next/standalone/.next/server/app/cortex.segments/_tree.segment.rsc +2 -2
  243. package/.next/standalone/.next/server/app/login/page_client-reference-manifest.js +1 -1
  244. package/.next/standalone/.next/server/app/login.html +1 -1
  245. package/.next/standalone/.next/server/app/login.rsc +2 -2
  246. package/.next/standalone/.next/server/app/login.segments/_full.segment.rsc +2 -2
  247. package/.next/standalone/.next/server/app/login.segments/_head.segment.rsc +1 -1
  248. package/.next/standalone/.next/server/app/login.segments/_index.segment.rsc +2 -2
  249. package/.next/standalone/.next/server/app/login.segments/_tree.segment.rsc +2 -2
  250. package/.next/standalone/.next/server/app/login.segments/login/__PAGE__.segment.rsc +1 -1
  251. package/.next/standalone/.next/server/app/login.segments/login.segment.rsc +1 -1
  252. package/.next/standalone/.next/server/app/m/page_client-reference-manifest.js +1 -1
  253. package/.next/standalone/.next/server/app/m/projects/page_client-reference-manifest.js +1 -1
  254. package/.next/standalone/.next/server/app/m/projects.html +1 -1
  255. package/.next/standalone/.next/server/app/m/projects.rsc +2 -2
  256. package/.next/standalone/.next/server/app/m/projects.segments/_full.segment.rsc +2 -2
  257. package/.next/standalone/.next/server/app/m/projects.segments/_head.segment.rsc +1 -1
  258. package/.next/standalone/.next/server/app/m/projects.segments/_index.segment.rsc +2 -2
  259. package/.next/standalone/.next/server/app/m/projects.segments/_tree.segment.rsc +2 -2
  260. package/.next/standalone/.next/server/app/m/projects.segments/m/projects/__PAGE__.segment.rsc +1 -1
  261. package/.next/standalone/.next/server/app/m/projects.segments/m/projects.segment.rsc +1 -1
  262. package/.next/standalone/.next/server/app/m/projects.segments/m.segment.rsc +1 -1
  263. package/.next/standalone/.next/server/app/m/sessions/[id]/page.js.nft.json +1 -1
  264. package/.next/standalone/.next/server/app/m/sessions/[id]/page_client-reference-manifest.js +1 -1
  265. package/.next/standalone/.next/server/app/m/sessions/page_client-reference-manifest.js +1 -1
  266. package/.next/standalone/.next/server/app/m/sessions.html +1 -1
  267. package/.next/standalone/.next/server/app/m/sessions.rsc +2 -2
  268. package/.next/standalone/.next/server/app/m/sessions.segments/_full.segment.rsc +2 -2
  269. package/.next/standalone/.next/server/app/m/sessions.segments/_head.segment.rsc +1 -1
  270. package/.next/standalone/.next/server/app/m/sessions.segments/_index.segment.rsc +2 -2
  271. package/.next/standalone/.next/server/app/m/sessions.segments/_tree.segment.rsc +2 -2
  272. package/.next/standalone/.next/server/app/m/sessions.segments/m/sessions/__PAGE__.segment.rsc +1 -1
  273. package/.next/standalone/.next/server/app/m/sessions.segments/m/sessions.segment.rsc +1 -1
  274. package/.next/standalone/.next/server/app/m/sessions.segments/m.segment.rsc +1 -1
  275. package/.next/standalone/.next/server/app/m/settings/page_client-reference-manifest.js +1 -1
  276. package/.next/standalone/.next/server/app/m/settings.html +1 -1
  277. package/.next/standalone/.next/server/app/m/settings.rsc +2 -2
  278. package/.next/standalone/.next/server/app/m/settings.segments/_full.segment.rsc +2 -2
  279. package/.next/standalone/.next/server/app/m/settings.segments/_head.segment.rsc +1 -1
  280. package/.next/standalone/.next/server/app/m/settings.segments/_index.segment.rsc +2 -2
  281. package/.next/standalone/.next/server/app/m/settings.segments/_tree.segment.rsc +2 -2
  282. package/.next/standalone/.next/server/app/m/settings.segments/m/settings/__PAGE__.segment.rsc +1 -1
  283. package/.next/standalone/.next/server/app/m/settings.segments/m/settings.segment.rsc +1 -1
  284. package/.next/standalone/.next/server/app/m/settings.segments/m.segment.rsc +1 -1
  285. package/.next/standalone/.next/server/app/m/terminal/page_client-reference-manifest.js +1 -1
  286. package/.next/standalone/.next/server/app/m/terminal.html +1 -1
  287. package/.next/standalone/.next/server/app/m/terminal.rsc +3 -3
  288. package/.next/standalone/.next/server/app/m/terminal.segments/_full.segment.rsc +3 -3
  289. package/.next/standalone/.next/server/app/m/terminal.segments/_head.segment.rsc +1 -1
  290. package/.next/standalone/.next/server/app/m/terminal.segments/_index.segment.rsc +2 -2
  291. package/.next/standalone/.next/server/app/m/terminal.segments/_tree.segment.rsc +2 -2
  292. package/.next/standalone/.next/server/app/m/terminal.segments/m/terminal/__PAGE__.segment.rsc +2 -2
  293. package/.next/standalone/.next/server/app/m/terminal.segments/m/terminal.segment.rsc +1 -1
  294. package/.next/standalone/.next/server/app/m/terminal.segments/m.segment.rsc +1 -1
  295. package/.next/standalone/.next/server/app/m.html +1 -1
  296. package/.next/standalone/.next/server/app/m.rsc +2 -2
  297. package/.next/standalone/.next/server/app/m.segments/_full.segment.rsc +2 -2
  298. package/.next/standalone/.next/server/app/m.segments/_head.segment.rsc +1 -1
  299. package/.next/standalone/.next/server/app/m.segments/_index.segment.rsc +2 -2
  300. package/.next/standalone/.next/server/app/m.segments/_tree.segment.rsc +2 -2
  301. package/.next/standalone/.next/server/app/m.segments/m/__PAGE__.segment.rsc +1 -1
  302. package/.next/standalone/.next/server/app/m.segments/m.segment.rsc +1 -1
  303. package/.next/standalone/.next/server/app/network.html +1 -1
  304. package/.next/standalone/.next/server/app/network.rsc +2 -2
  305. package/.next/standalone/.next/server/app/network.segments/!KGRlc2t0b3Ap/network/__PAGE__.segment.rsc +1 -1
  306. package/.next/standalone/.next/server/app/network.segments/!KGRlc2t0b3Ap/network.segment.rsc +1 -1
  307. package/.next/standalone/.next/server/app/network.segments/!KGRlc2t0b3Ap.segment.rsc +1 -1
  308. package/.next/standalone/.next/server/app/network.segments/_full.segment.rsc +2 -2
  309. package/.next/standalone/.next/server/app/network.segments/_head.segment.rsc +1 -1
  310. package/.next/standalone/.next/server/app/network.segments/_index.segment.rsc +2 -2
  311. package/.next/standalone/.next/server/app/network.segments/_tree.segment.rsc +2 -2
  312. package/.next/standalone/.next/server/app/projects.html +1 -1
  313. package/.next/standalone/.next/server/app/projects.rsc +2 -2
  314. package/.next/standalone/.next/server/app/projects.segments/!KGRlc2t0b3Ap/projects/__PAGE__.segment.rsc +1 -1
  315. package/.next/standalone/.next/server/app/projects.segments/!KGRlc2t0b3Ap/projects.segment.rsc +1 -1
  316. package/.next/standalone/.next/server/app/projects.segments/!KGRlc2t0b3Ap.segment.rsc +1 -1
  317. package/.next/standalone/.next/server/app/projects.segments/_full.segment.rsc +2 -2
  318. package/.next/standalone/.next/server/app/projects.segments/_head.segment.rsc +1 -1
  319. package/.next/standalone/.next/server/app/projects.segments/_index.segment.rsc +2 -2
  320. package/.next/standalone/.next/server/app/projects.segments/_tree.segment.rsc +2 -2
  321. package/.next/standalone/.next/server/app/sessions.html +1 -1
  322. package/.next/standalone/.next/server/app/sessions.rsc +2 -2
  323. package/.next/standalone/.next/server/app/sessions.segments/!KGRlc2t0b3Ap/sessions/__PAGE__.segment.rsc +1 -1
  324. package/.next/standalone/.next/server/app/sessions.segments/!KGRlc2t0b3Ap/sessions.segment.rsc +1 -1
  325. package/.next/standalone/.next/server/app/sessions.segments/!KGRlc2t0b3Ap.segment.rsc +1 -1
  326. package/.next/standalone/.next/server/app/sessions.segments/_full.segment.rsc +2 -2
  327. package/.next/standalone/.next/server/app/sessions.segments/_head.segment.rsc +1 -1
  328. package/.next/standalone/.next/server/app/sessions.segments/_index.segment.rsc +2 -2
  329. package/.next/standalone/.next/server/app/sessions.segments/_tree.segment.rsc +2 -2
  330. package/.next/standalone/.next/server/app/settings.html +1 -1
  331. package/.next/standalone/.next/server/app/settings.rsc +2 -2
  332. package/.next/standalone/.next/server/app/settings.segments/!KGRlc2t0b3Ap/settings/__PAGE__.segment.rsc +1 -1
  333. package/.next/standalone/.next/server/app/settings.segments/!KGRlc2t0b3Ap/settings.segment.rsc +1 -1
  334. package/.next/standalone/.next/server/app/settings.segments/!KGRlc2t0b3Ap.segment.rsc +1 -1
  335. package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +2 -2
  336. package/.next/standalone/.next/server/app/settings.segments/_head.segment.rsc +1 -1
  337. package/.next/standalone/.next/server/app/settings.segments/_index.segment.rsc +2 -2
  338. package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  339. package/.next/standalone/.next/server/app/terminal.html +1 -1
  340. package/.next/standalone/.next/server/app/terminal.rsc +3 -3
  341. package/.next/standalone/.next/server/app/terminal.segments/!KGRlc2t0b3Ap/terminal/__PAGE__.segment.rsc +2 -2
  342. package/.next/standalone/.next/server/app/terminal.segments/!KGRlc2t0b3Ap/terminal.segment.rsc +1 -1
  343. package/.next/standalone/.next/server/app/terminal.segments/!KGRlc2t0b3Ap.segment.rsc +1 -1
  344. package/.next/standalone/.next/server/app/terminal.segments/_full.segment.rsc +3 -3
  345. package/.next/standalone/.next/server/app/terminal.segments/_head.segment.rsc +1 -1
  346. package/.next/standalone/.next/server/app/terminal.segments/_index.segment.rsc +2 -2
  347. package/.next/standalone/.next/server/app/terminal.segments/_tree.segment.rsc +2 -2
  348. package/.next/standalone/.next/server/app/vr/page/react-loadable-manifest.json +1 -1
  349. package/.next/standalone/.next/server/app/vr/page_client-reference-manifest.js +1 -1
  350. package/.next/standalone/.next/server/app/vr.html +1 -1
  351. package/.next/standalone/.next/server/app/vr.rsc +3 -3
  352. package/.next/standalone/.next/server/app/vr.segments/_full.segment.rsc +3 -3
  353. package/.next/standalone/.next/server/app/vr.segments/_head.segment.rsc +1 -1
  354. package/.next/standalone/.next/server/app/vr.segments/_index.segment.rsc +2 -2
  355. package/.next/standalone/.next/server/app/vr.segments/_tree.segment.rsc +2 -2
  356. package/.next/standalone/.next/server/app/vr.segments/vr/__PAGE__.segment.rsc +2 -2
  357. package/.next/standalone/.next/server/app/vr.segments/vr.segment.rsc +1 -1
  358. package/.next/standalone/.next/server/app/workspaces.html +1 -1
  359. package/.next/standalone/.next/server/app/workspaces.rsc +2 -2
  360. package/.next/standalone/.next/server/app/workspaces.segments/!KGRlc2t0b3Ap/workspaces/__PAGE__.segment.rsc +1 -1
  361. package/.next/standalone/.next/server/app/workspaces.segments/!KGRlc2t0b3Ap/workspaces.segment.rsc +1 -1
  362. package/.next/standalone/.next/server/app/workspaces.segments/!KGRlc2t0b3Ap.segment.rsc +1 -1
  363. package/.next/standalone/.next/server/app/workspaces.segments/_full.segment.rsc +2 -2
  364. package/.next/standalone/.next/server/app/workspaces.segments/_head.segment.rsc +1 -1
  365. package/.next/standalone/.next/server/app/workspaces.segments/_index.segment.rsc +2 -2
  366. package/.next/standalone/.next/server/app/workspaces.segments/_tree.segment.rsc +2 -2
  367. package/.next/standalone/.next/server/app-paths-manifest.json +1 -0
  368. package/.next/standalone/.next/server/chunks/[root-of-the-server]__00e90fc6._.js +98 -0
  369. package/.next/standalone/.next/server/chunks/[root-of-the-server]__01ab8675._.js +98 -0
  370. package/.next/standalone/.next/server/chunks/[root-of-the-server]__03974f05._.js +98 -0
  371. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__bb331da9._.js → [root-of-the-server]__046c9b91._.js} +3 -3
  372. package/.next/standalone/.next/server/chunks/[root-of-the-server]__04ae6bf0._.js +98 -0
  373. package/.next/standalone/.next/server/chunks/[root-of-the-server]__056fa416._.js +1 -1
  374. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ac4ea3f._.js +3 -0
  375. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0b8e64cb._.js +98 -0
  376. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__d95165f0._.js → [root-of-the-server]__0facd39e._.js} +3 -3
  377. package/.next/standalone/.next/server/chunks/[root-of-the-server]__10bc76a3._.js +3 -0
  378. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__9d68157b._.js → [root-of-the-server]__115f3934._.js} +3 -3
  379. package/.next/standalone/.next/server/chunks/[root-of-the-server]__11f155f1._.js +3 -0
  380. package/.next/standalone/.next/server/chunks/[root-of-the-server]__160e7c73._.js +22 -33
  381. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__91e23c96._.js → [root-of-the-server]__17a3b966._.js} +3 -3
  382. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__277d9445._.js → [root-of-the-server]__17d3a2b2._.js} +3 -3
  383. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1a86c055._.js +98 -0
  384. package/.next/standalone/.next/server/chunks/[root-of-the-server]__20b5e9c4._.js +3 -0
  385. package/.next/standalone/.next/server/chunks/[root-of-the-server]__28d6fbd8._.js +98 -0
  386. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__04f04898._.js → [root-of-the-server]__2a3f866b._.js} +2 -2
  387. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__a95bb38b._.js → [root-of-the-server]__316617e7._.js} +2 -2
  388. package/.next/standalone/.next/server/chunks/[root-of-the-server]__32ad8f71._.js +98 -0
  389. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__6fe5e6c8._.js → [root-of-the-server]__35457394._.js} +2 -2
  390. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1f054c65._.js → [root-of-the-server]__35de78e6._.js} +3 -3
  391. package/.next/standalone/.next/server/chunks/[root-of-the-server]__3685ffcb._.js +98 -0
  392. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__614458b7._.js → [root-of-the-server]__38954988._.js} +3 -3
  393. package/.next/standalone/.next/server/chunks/[root-of-the-server]__426ad936._.js +106 -0
  394. package/.next/standalone/.next/server/chunks/[root-of-the-server]__4985c034._.js +98 -0
  395. package/.next/standalone/.next/server/chunks/[root-of-the-server]__5c6ce9ed._.js +98 -0
  396. package/.next/standalone/.next/server/chunks/[root-of-the-server]__5cebe58a._.js +98 -0
  397. package/.next/standalone/.next/server/chunks/[root-of-the-server]__5d5e4789._.js +98 -0
  398. package/.next/standalone/.next/server/chunks/[root-of-the-server]__65676930._.js +3 -0
  399. package/.next/standalone/.next/server/chunks/[root-of-the-server]__67cab326._.js +58 -0
  400. package/.next/standalone/.next/server/chunks/[root-of-the-server]__698c6f01._.js +98 -0
  401. package/.next/standalone/.next/server/chunks/[root-of-the-server]__6c64af29._.js +131 -0
  402. package/.next/standalone/.next/server/chunks/[root-of-the-server]__73aed9f5._.js +98 -0
  403. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__ac84b704._.js → [root-of-the-server]__79b6a9bb._.js} +3 -3
  404. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__e56abacf._.js → [root-of-the-server]__7db704c6._.js} +4 -4
  405. package/.next/standalone/.next/server/chunks/[root-of-the-server]__812ca02b._.js +98 -0
  406. package/.next/standalone/.next/server/chunks/[root-of-the-server]__821f50fa._.js +98 -0
  407. package/.next/standalone/.next/server/chunks/[root-of-the-server]__8716b86e._.js +98 -0
  408. package/.next/standalone/.next/server/chunks/[root-of-the-server]__884ef754._.js +98 -0
  409. package/.next/standalone/.next/server/chunks/[root-of-the-server]__88cdbd68._.js +98 -0
  410. package/.next/standalone/.next/server/chunks/[root-of-the-server]__89d9aba9._.js +98 -0
  411. package/.next/standalone/.next/server/chunks/[root-of-the-server]__8d536cb5._.js +98 -0
  412. package/.next/standalone/.next/server/chunks/[root-of-the-server]__8df8c5d1._.js +98 -0
  413. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__2d7a454e._.js → [root-of-the-server]__8f2ccc41._.js} +3 -3
  414. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__2a2c5fc5._.js → [root-of-the-server]__95c9d682._.js} +4 -4
  415. package/.next/standalone/.next/server/chunks/[root-of-the-server]__9e5d7774._.js +98 -0
  416. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__cc6e2885._.js → [root-of-the-server]__9edcff87._.js} +2 -2
  417. package/.next/standalone/.next/server/chunks/[root-of-the-server]__a049dfc2._.js +98 -0
  418. package/.next/standalone/.next/server/chunks/[root-of-the-server]__a5b4bb9a._.js +98 -0
  419. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__929ea03a._.js → [root-of-the-server]__a83262a1._.js} +2 -2
  420. package/.next/standalone/.next/server/chunks/[root-of-the-server]__a9cd1240._.js +98 -0
  421. package/.next/standalone/.next/server/chunks/[root-of-the-server]__a9d7f822._.js +98 -0
  422. package/.next/standalone/.next/server/chunks/[root-of-the-server]__ad08c221._.js +98 -0
  423. package/.next/standalone/.next/server/chunks/[root-of-the-server]__ad585f2f._.js +98 -0
  424. package/.next/standalone/.next/server/chunks/[root-of-the-server]__afcb8f7d._.js +98 -0
  425. package/.next/standalone/.next/server/chunks/[root-of-the-server]__bc250d43._.js +98 -0
  426. package/.next/standalone/.next/server/chunks/[root-of-the-server]__bce2a6e7._.js +98 -0
  427. package/.next/standalone/.next/server/chunks/[root-of-the-server]__c011bf91._.js +98 -0
  428. package/.next/standalone/.next/server/chunks/[root-of-the-server]__c0ac2895._.js +3 -0
  429. package/.next/standalone/.next/server/chunks/[root-of-the-server]__c130a00c._.js +1 -1
  430. package/.next/standalone/.next/server/chunks/[root-of-the-server]__c37d6380._.js +3 -0
  431. package/.next/standalone/.next/server/chunks/[root-of-the-server]__cae392eb._.js +98 -0
  432. package/.next/standalone/.next/server/chunks/[root-of-the-server]__cc2616bb._.js +3 -3
  433. package/.next/standalone/.next/server/chunks/[root-of-the-server]__d501fa9b._.js +98 -0
  434. package/.next/standalone/.next/server/chunks/[root-of-the-server]__d59c6c15._.js +98 -0
  435. package/.next/standalone/.next/server/chunks/[root-of-the-server]__d5c1db32._.js +98 -0
  436. package/.next/standalone/.next/server/chunks/[root-of-the-server]__d5d92527._.js +1 -1
  437. package/.next/standalone/.next/server/chunks/[root-of-the-server]__dba60c86._.js +98 -0
  438. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__2384f98e._.js → [root-of-the-server]__de14b9ae._.js} +3 -3
  439. package/.next/standalone/.next/server/chunks/[root-of-the-server]__e10643d1._.js +98 -0
  440. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__00fdfbda._.js → [root-of-the-server]__e2a996e5._.js} +2 -2
  441. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__4d903941._.js → [root-of-the-server]__e3477417._.js} +3 -3
  442. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__32dc5513._.js → [root-of-the-server]__e4db362e._.js} +2 -2
  443. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__49e42a3a._.js → [root-of-the-server]__e4e70b86._.js} +3 -3
  444. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__ac39ecc7._.js → [root-of-the-server]__e54925af._.js} +3 -3
  445. package/.next/standalone/.next/server/chunks/[root-of-the-server]__e8edc5b0._.js +98 -0
  446. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__eafd040b._.js → [root-of-the-server]__eab4d83b._.js} +2 -2
  447. package/.next/standalone/.next/server/chunks/[root-of-the-server]__ead29015._.js +1 -1
  448. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__194955d4._.js → [root-of-the-server]__f056fd83._.js} +3 -3
  449. package/.next/standalone/.next/server/chunks/[root-of-the-server]__f0e99572._.js +98 -0
  450. package/.next/standalone/.next/server/chunks/[root-of-the-server]__fe1e16d0._.js +98 -0
  451. package/.next/standalone/.next/server/chunks/[root-of-the-server]__ff9cd277._.js +98 -0
  452. package/.next/standalone/.next/server/chunks/[root-of-the-server]__ffaea2ce._.js +98 -0
  453. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_wizard_chart_route_actions_888e2ec1.js +3 -0
  454. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__2cffc362._.js +3 -0
  455. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__f1050870._.js → [root-of-the-server]__47c97637._.js} +2 -2
  456. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__66aca5d4._.js +1 -1
  457. package/.next/standalone/.next/server/chunks/ssr/_17b946fd._.js +3 -0
  458. package/.next/standalone/.next/server/chunks/ssr/_2a1d79e7._.js +1 -1
  459. package/.next/standalone/.next/server/chunks/ssr/_5c3c4cfa._.js +7 -5
  460. package/.next/standalone/.next/server/chunks/ssr/_ba432382._.js +7 -5
  461. package/.next/standalone/.next/server/chunks/ssr/src_app_(desktop)_cortex_page_tsx_0f33d8b3._.js +1 -1
  462. package/.next/standalone/.next/server/chunks/ssr/src_app_(desktop)_terminal_page_tsx_de5e8d85._.js +4 -4
  463. package/.next/standalone/.next/server/edge/chunks/[root-of-the-server]__90eeddae._.js +1 -1
  464. package/.next/standalone/.next/server/middleware-manifest.json +5 -5
  465. package/.next/standalone/.next/server/pages/404.html +1 -1
  466. package/.next/standalone/.next/server/pages/500.html +2 -2
  467. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  468. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  469. package/.next/standalone/.next/static/chunks/0852575eb90c1e8d.js +85 -0
  470. package/.next/standalone/.next/static/chunks/{74d0ac0b1d0a1b79.js → 10b00b4f66102dcf.js} +1 -1
  471. package/.next/standalone/.next/static/chunks/{16eb77953dee9ea3.js → 19c71a8376c23c58.js} +1 -1
  472. package/.next/standalone/.next/static/chunks/2b769d1597e4fc1c.css +3 -0
  473. package/.next/standalone/.next/static/chunks/{3e91fc608659c524.js → 350271fe79509caf.js} +1 -1
  474. package/.next/standalone/.next/static/chunks/{70423c7afd8abf5f.js → 597847c22200c212.js} +1 -1
  475. package/.next/standalone/.next/static/chunks/{fb0abd1933b2b2e1.js → 7f6a14f1849fa94d.js} +1 -1
  476. package/.next/standalone/.next/static/chunks/{7ecd9bbb0ce4d68a.js → b7c8fe9b7275a84f.js} +1 -1
  477. package/.next/standalone/.next/static/chunks/{6245135a7afb8c7b.js → c9b10fc55516d142.js} +8 -6
  478. package/.next/standalone/.next/static/chunks/d0065f48eab94944.js +1 -0
  479. package/.next/standalone/.next/static/chunks/{180c1b9ff31b979f.js → f7b34c807badf95d.js} +8 -6
  480. package/.next/standalone/.spaces/cortex-context.md +50 -144
  481. package/.next/standalone/LICENSE +661 -661
  482. package/.next/standalone/NOTICE +5 -5
  483. package/.next/standalone/README.md +131 -131
  484. package/.next/standalone/bin/cortex-hook.js +79 -79
  485. package/.next/standalone/bin/cortex-hook.sh +62 -62
  486. package/.next/standalone/bin/cortex-learn-hook.js +138 -138
  487. package/.next/standalone/bin/cortex-mcp.js +60 -60
  488. package/.next/standalone/bin/cortex-pi-extension.ts +170 -170
  489. package/.next/standalone/bin/fix-standalone-externals.js +79 -79
  490. package/.next/standalone/bin/lib/auto-setup.js +110 -110
  491. package/.next/standalone/bin/mdns-service.js +171 -171
  492. package/.next/standalone/bin/postinstall.js +35 -35
  493. package/.next/standalone/bin/setup-admin.js +195 -195
  494. package/.next/standalone/bin/spaces-dev.js +247 -247
  495. package/.next/standalone/bin/spaces-install.js +660 -660
  496. package/.next/standalone/bin/spaces-postinstall.js +50 -50
  497. package/.next/standalone/bin/spaces-reset-totp.js +50 -50
  498. package/.next/standalone/bin/spaces-service.js +1046 -1046
  499. package/.next/standalone/bin/spaces-setup.js +253 -253
  500. package/.next/standalone/bin/spaces.js +808 -805
  501. package/.next/standalone/bin/ssh-auth-keys.sh +68 -68
  502. package/.next/standalone/bin/terminal-server.js +2819 -2781
  503. package/.next/standalone/cortex-hook-debug.log +57 -23
  504. package/.next/standalone/docker-compose.yml +28 -28
  505. package/.next/standalone/docs/architecture.md +387 -387
  506. package/.next/standalone/docs/cortex-integration-reference.md +268 -268
  507. package/.next/standalone/docs/cortex.md +293 -293
  508. package/.next/standalone/docs/getting-started.md +96 -96
  509. package/.next/standalone/docs/plans/2026-02-24-multi-agent-sessions-design.md +133 -133
  510. package/.next/standalone/docs/plans/2026-02-24-multi-agent-sessions-plan.md +959 -959
  511. package/.next/standalone/docs/plans/2026-03-02-security-audit.md +229 -229
  512. package/.next/standalone/docs/plans/2026-03-07-service-command-design.md +146 -146
  513. package/.next/standalone/docs/plans/2026-03-07-service-command-plan.md +254 -254
  514. package/.next/standalone/docs/server-install.md +564 -564
  515. package/.next/standalone/docs/social-card.html +150 -150
  516. package/.next/standalone/docs/superpowers/plans/2026-03-12-spaces-cortex.md +5270 -5270
  517. package/.next/standalone/docs/superpowers/plans/2026-03-13-cortex-wiring.md +1387 -1387
  518. package/.next/standalone/docs/superpowers/plans/2026-03-14-cortex-v2-entity-graph.md +1923 -1923
  519. package/.next/standalone/docs/superpowers/plans/2026-03-14-cortex-v2-knowledge-evolution.md +1113 -1113
  520. package/.next/standalone/docs/superpowers/plans/2026-03-15-cortex-v2-boundary-engine.md +853 -853
  521. package/.next/standalone/docs/superpowers/plans/2026-03-15-cortex-v2-context-engine.md +1274 -1274
  522. package/.next/standalone/docs/superpowers/plans/2026-03-15-cortex-v2-signal-ingestion.md +933 -933
  523. package/.next/standalone/docs/superpowers/plans/2026-03-16-cortex-lobes.md +1080 -1080
  524. package/.next/standalone/docs/superpowers/plans/2026-03-16-cortex-v2-gravity-system.md +768 -768
  525. package/.next/standalone/docs/superpowers/plans/2026-03-16-cortex-v2-ui.md +1108 -1108
  526. package/.next/standalone/docs/superpowers/plans/2026-03-18-cortex-ui-integration.md +1846 -1846
  527. package/.next/standalone/docs/superpowers/plans/2026-03-19-vr-phase1-shell.md +1639 -1639
  528. package/.next/standalone/docs/superpowers/plans/2026-03-27-dockview-pane-layout.md +98 -98
  529. package/.next/standalone/docs/superpowers/specs/2026-03-11-universe-view-design.md +320 -320
  530. package/.next/standalone/docs/superpowers/specs/2026-03-12-spaces-brain-design.md +720 -720
  531. package/.next/standalone/docs/superpowers/specs/2026-03-13-cortex-wiring-design.md +268 -268
  532. package/.next/standalone/docs/superpowers/specs/2026-03-14-cortex-v2-design.md +623 -623
  533. package/.next/standalone/docs/superpowers/specs/2026-03-16-cortex-lobes-design.md +263 -263
  534. package/.next/standalone/docs/superpowers/specs/2026-03-16-cortex-v2-ui-design.md +240 -240
  535. package/.next/standalone/docs/superpowers/specs/2026-03-16-pane-ux-design.md +77 -77
  536. package/.next/standalone/docs/superpowers/specs/2026-03-18-cortex-ui-integration-design.md +341 -341
  537. package/.next/standalone/docs/superpowers/specs/2026-03-19-vr-phase1-shell-design.md +288 -288
  538. package/.next/standalone/docs/superpowers/specs/2026-03-27-pane-diff-review-and-project-wizard-design.md +322 -322
  539. package/.next/standalone/docs/tiers.md +104 -104
  540. package/.next/standalone/eslint.config.mjs +18 -18
  541. package/.next/standalone/next.config.ts +20 -20
  542. package/.next/standalone/nginx.conf +53 -53
  543. package/.next/standalone/node_modules/@img/sharp-win32-x64/lib/sharp-win32-x64.node +0 -0
  544. package/.next/standalone/node_modules/@img/{sharp-linux-x64 → sharp-win32-x64}/package.json +39 -46
  545. package/.next/standalone/package-lock.json +14985 -14986
  546. package/.next/standalone/package.json +111 -111
  547. package/.next/standalone/postcss.config.mjs +7 -7
  548. package/.next/standalone/scripts/rebuild.cmd +65 -65
  549. package/.next/standalone/scripts/rebuild.sh +59 -59
  550. package/.next/standalone/server.js +1 -1
  551. package/.next/standalone/src/app/(desktop)/admin/analytics/page.tsx +266 -266
  552. package/.next/standalone/src/app/(desktop)/admin/users/page.tsx +399 -399
  553. package/.next/standalone/src/app/(desktop)/analytics/page.tsx +166 -166
  554. package/.next/standalone/src/app/(desktop)/cortex/page.tsx +81 -81
  555. package/.next/standalone/src/app/(desktop)/dashboard-client.tsx +56 -56
  556. package/.next/standalone/src/app/(desktop)/layout.tsx +18 -18
  557. package/.next/standalone/src/app/(desktop)/network/page.tsx +137 -137
  558. package/.next/standalone/src/app/(desktop)/page.tsx +17 -17
  559. package/.next/standalone/src/app/(desktop)/projects/page.tsx +68 -68
  560. package/.next/standalone/src/app/(desktop)/sessions/[id]/page.tsx +519 -519
  561. package/.next/standalone/src/app/(desktop)/sessions/page.tsx +145 -145
  562. package/.next/standalone/src/app/(desktop)/settings/page.tsx +446 -446
  563. package/.next/standalone/src/app/(desktop)/terminal/layout.tsx +7 -7
  564. package/.next/standalone/src/app/(desktop)/terminal/page.tsx +1330 -1291
  565. package/.next/standalone/src/app/(desktop)/terminal/pane/[id]/page.tsx +211 -211
  566. package/.next/standalone/src/app/(desktop)/terminal/remote/[nodeId]/[workspaceId]/page.tsx +252 -252
  567. package/.next/standalone/src/app/(desktop)/workspaces/page.tsx +12 -12
  568. package/.next/standalone/src/app/api/admin/analytics/route.ts +10 -10
  569. package/.next/standalone/src/app/api/admin/users/[id]/route.ts +20 -20
  570. package/.next/standalone/src/app/api/admin/users/route.ts +15 -15
  571. package/.next/standalone/src/app/api/analytics/overview/route.ts +80 -80
  572. package/.next/standalone/src/app/api/auth/login/route.ts +10 -10
  573. package/.next/standalone/src/app/api/auth/logout/route.ts +9 -9
  574. package/.next/standalone/src/app/api/auth/me/route.ts +22 -22
  575. package/.next/standalone/src/app/api/auth/totp/setup/route.ts +10 -10
  576. package/.next/standalone/src/app/api/auth/totp/status/route.ts +10 -10
  577. package/.next/standalone/src/app/api/auth/totp/verify/route.ts +10 -10
  578. package/.next/standalone/src/app/api/benchmark/lobes/route.ts +16 -16
  579. package/.next/standalone/src/app/api/benchmark/run/route.ts +113 -92
  580. package/.next/standalone/src/app/api/benchmark/runs/[id]/route.ts +26 -26
  581. package/.next/standalone/src/app/api/benchmark/runs/route.ts +16 -16
  582. package/.next/standalone/src/app/api/benchmark/status/route.ts +35 -35
  583. package/.next/standalone/src/app/api/bulk/route.ts +34 -34
  584. package/.next/standalone/src/app/api/chat/route.ts +85 -85
  585. package/.next/standalone/src/app/api/config/route.ts +30 -30
  586. package/.next/standalone/src/app/api/cortex/context/route.ts +78 -78
  587. package/.next/standalone/src/app/api/cortex/curation/assess/route.ts +27 -27
  588. package/.next/standalone/src/app/api/cortex/curation/publish/route.ts +23 -23
  589. package/.next/standalone/src/app/api/cortex/curation/refine/route.ts +23 -23
  590. package/.next/standalone/src/app/api/cortex/curation/review/route.ts +29 -29
  591. package/.next/standalone/src/app/api/cortex/curation/seed/route.ts +23 -23
  592. package/.next/standalone/src/app/api/cortex/export/route.ts +40 -40
  593. package/.next/standalone/src/app/api/cortex/federation/pending/route.ts +20 -20
  594. package/.next/standalone/src/app/api/cortex/federation/resolve/route.ts +43 -43
  595. package/.next/standalone/src/app/api/cortex/federation/search/route.ts +35 -35
  596. package/.next/standalone/src/app/api/cortex/federation/teach/route.ts +76 -76
  597. package/.next/standalone/src/app/api/cortex/graph/edges/route.ts +112 -112
  598. package/.next/standalone/src/app/api/cortex/graph/entities/[id]/route.ts +73 -73
  599. package/.next/standalone/src/app/api/cortex/graph/entities/route.ts +75 -75
  600. package/.next/standalone/src/app/api/cortex/graph/populate/route.ts +203 -203
  601. package/.next/standalone/src/app/api/cortex/import/route.ts +75 -75
  602. package/.next/standalone/src/app/api/cortex/import/status/route.ts +15 -15
  603. package/.next/standalone/src/app/api/cortex/ingest/bootstrap/route.ts +39 -39
  604. package/.next/standalone/src/app/api/cortex/ingest/status/route.ts +15 -15
  605. package/.next/standalone/src/app/api/cortex/knowledge/[id]/route.ts +91 -91
  606. package/.next/standalone/src/app/api/cortex/knowledge/route.ts +97 -97
  607. package/.next/standalone/src/app/api/cortex/lobes/[id]/route.ts +67 -67
  608. package/.next/standalone/src/app/api/cortex/lobes/route.ts +22 -22
  609. package/.next/standalone/src/app/api/cortex/lobes/share/route.ts +80 -80
  610. package/.next/standalone/src/app/api/cortex/marketplace/browse/route.ts +43 -43
  611. package/.next/standalone/src/app/api/cortex/marketplace/preview/route.ts +46 -46
  612. package/.next/standalone/src/app/api/cortex/mcp/call/route.ts +11 -11
  613. package/.next/standalone/src/app/api/cortex/mcp/tools/route.ts +8 -8
  614. package/.next/standalone/src/app/api/cortex/search/route.ts +57 -45
  615. package/.next/standalone/src/app/api/cortex/settings/route.ts +35 -35
  616. package/.next/standalone/src/app/api/cortex/status/route.ts +169 -169
  617. package/.next/standalone/src/app/api/cortex/timeline/route.ts +42 -42
  618. package/.next/standalone/src/app/api/cortex/usage/route.ts +31 -31
  619. package/.next/standalone/src/app/api/cortex/workspace/[id]/context/route.ts +41 -41
  620. package/.next/standalone/src/app/api/events/route.ts +40 -40
  621. package/.next/standalone/src/app/api/files/route.ts +187 -187
  622. package/.next/standalone/src/app/api/folders/route.ts +107 -97
  623. package/.next/standalone/src/app/api/network/connect-callback/route.ts +11 -11
  624. package/.next/standalone/src/app/api/network/connect-request/[id]/route.ts +11 -11
  625. package/.next/standalone/src/app/api/network/connect-request/route.ts +17 -17
  626. package/.next/standalone/src/app/api/network/discovered/route.ts +9 -9
  627. package/.next/standalone/src/app/api/network/handshake/route.ts +25 -25
  628. package/.next/standalone/src/app/api/network/health/route.ts +10 -10
  629. package/.next/standalone/src/app/api/network/identity/route.ts +15 -15
  630. package/.next/standalone/src/app/api/network/keys/[id]/route.ts +10 -10
  631. package/.next/standalone/src/app/api/network/keys/route.ts +15 -15
  632. package/.next/standalone/src/app/api/network/nodes/[id]/route.ts +15 -15
  633. package/.next/standalone/src/app/api/network/nodes/check/route.ts +9 -9
  634. package/.next/standalone/src/app/api/network/nodes/route.ts +15 -15
  635. package/.next/standalone/src/app/api/network/panes/[id]/route.ts +78 -62
  636. package/.next/standalone/src/app/api/network/panes/route.ts +61 -50
  637. package/.next/standalone/src/app/api/network/projects/route.ts +32 -25
  638. package/.next/standalone/src/app/api/network/proxy/[nodeId]/[...path]/route.ts +25 -25
  639. package/.next/standalone/src/app/api/network/search/route.ts +45 -38
  640. package/.next/standalone/src/app/api/network/sessions/[id]/messages/route.ts +43 -36
  641. package/.next/standalone/src/app/api/network/sessions/[id]/route.ts +41 -34
  642. package/.next/standalone/src/app/api/network/sessions/route.ts +50 -43
  643. package/.next/standalone/src/app/api/network/terminal/token/route.ts +10 -10
  644. package/.next/standalone/src/app/api/network/workspaces/[id]/route.ts +80 -71
  645. package/.next/standalone/src/app/api/network/workspaces/route.ts +87 -85
  646. package/.next/standalone/src/app/api/panes/[id]/diff/route.ts +121 -121
  647. package/.next/standalone/src/app/api/panes/[id]/route.ts +60 -60
  648. package/.next/standalone/src/app/api/panes/route.ts +39 -39
  649. package/.next/standalone/src/app/api/projects/route.ts +13 -13
  650. package/.next/standalone/src/app/api/proxy/models/[modelId]/[...path]/route.ts +80 -80
  651. package/.next/standalone/src/app/api/proxy/models/[modelId]/status/route.ts +33 -33
  652. package/.next/standalone/src/app/api/search/route.ts +47 -47
  653. package/.next/standalone/src/app/api/sessions/[id]/chat/route.ts +120 -120
  654. package/.next/standalone/src/app/api/sessions/[id]/messages/route.ts +34 -34
  655. package/.next/standalone/src/app/api/sessions/[id]/route.ts +73 -73
  656. package/.next/standalone/src/app/api/sessions/route.ts +64 -64
  657. package/.next/standalone/src/app/api/sync/route.ts +24 -24
  658. package/.next/standalone/src/app/api/tags/route.ts +35 -35
  659. package/.next/standalone/src/app/api/tier/route.ts +16 -16
  660. package/.next/standalone/src/app/api/updates/route.ts +65 -65
  661. package/.next/standalone/src/app/api/whisper/config/route.ts +50 -42
  662. package/.next/standalone/src/app/api/whisper/route.ts +91 -91
  663. package/.next/standalone/src/app/api/wizard/chart/route.ts +129 -0
  664. package/.next/standalone/src/app/api/wizard/chat/route.ts +113 -113
  665. package/.next/standalone/src/app/api/workspaces/[id]/context/[key]/route.ts +39 -39
  666. package/.next/standalone/src/app/api/workspaces/[id]/context/route.ts +28 -28
  667. package/.next/standalone/src/app/api/workspaces/[id]/messages/[msgId]/route.ts +17 -17
  668. package/.next/standalone/src/app/api/workspaces/[id]/messages/route.ts +39 -39
  669. package/.next/standalone/src/app/api/workspaces/[id]/route.ts +47 -47
  670. package/.next/standalone/src/app/api/workspaces/[id]/sessions/route.ts +62 -62
  671. package/.next/standalone/src/app/api/workspaces/route.ts +79 -79
  672. package/.next/standalone/src/app/globals.css +88 -88
  673. package/.next/standalone/src/app/layout.tsx +33 -33
  674. package/.next/standalone/src/app/login/layout.tsx +7 -7
  675. package/.next/standalone/src/app/login/page.tsx +315 -315
  676. package/.next/standalone/src/app/m/layout.tsx +16 -16
  677. package/.next/standalone/src/app/m/page.tsx +118 -118
  678. package/.next/standalone/src/app/m/projects/page.tsx +64 -64
  679. package/.next/standalone/src/app/m/sessions/[id]/page.tsx +168 -168
  680. package/.next/standalone/src/app/m/sessions/page.tsx +177 -177
  681. package/.next/standalone/src/app/m/settings/page.tsx +230 -230
  682. package/.next/standalone/src/app/m/terminal/page.tsx +413 -413
  683. package/.next/standalone/src/app/vr/page.tsx +21 -21
  684. package/.next/standalone/src/app/vr/vr-app.tsx +163 -163
  685. package/.next/standalone/src/app/vr/vr-controls.tsx +139 -139
  686. package/.next/standalone/src/app/vr/vr-door.tsx +82 -82
  687. package/.next/standalone/src/app/vr/vr-environment.tsx +71 -71
  688. package/.next/standalone/src/app/vr/vr-gaze.tsx +89 -89
  689. package/.next/standalone/src/app/vr/vr-layout.ts +49 -49
  690. package/.next/standalone/src/app/vr/vr-lobby.tsx +97 -97
  691. package/.next/standalone/src/app/vr/vr-pane.tsx +195 -195
  692. package/.next/standalone/src/app/vr/vr-room.tsx +79 -79
  693. package/.next/standalone/src/app/vr/vr-terminal.tsx +303 -303
  694. package/.next/standalone/src/components/auth/totp-gate.tsx +183 -183
  695. package/.next/standalone/src/components/bus/activity-panel.tsx +261 -261
  696. package/.next/standalone/src/components/common/color-picker.tsx +35 -35
  697. package/.next/standalone/src/components/common/dev-directory-picker.tsx +339 -339
  698. package/.next/standalone/src/components/common/folder-picker.tsx +200 -200
  699. package/.next/standalone/src/components/common/tag-picker.tsx +190 -190
  700. package/.next/standalone/src/components/common/workspace-picker.tsx +113 -113
  701. package/.next/standalone/src/components/cortex/benchmark-tab.tsx +894 -880
  702. package/.next/standalone/src/components/cortex/constants.ts +29 -29
  703. package/.next/standalone/src/components/cortex/cortex-dashboard.tsx +304 -304
  704. package/.next/standalone/src/components/cortex/cortex-indicator.tsx +44 -44
  705. package/.next/standalone/src/components/cortex/cortex-panel.tsx +140 -140
  706. package/.next/standalone/src/components/cortex/cortex-settings.tsx +280 -280
  707. package/.next/standalone/src/components/cortex/curation-tab.tsx +810 -810
  708. package/.next/standalone/src/components/cortex/entity-detail.tsx +101 -101
  709. package/.next/standalone/src/components/cortex/entity-graph.tsx +382 -382
  710. package/.next/standalone/src/components/cortex/import-dialog.tsx +212 -212
  711. package/.next/standalone/src/components/cortex/injection-badge.tsx +72 -72
  712. package/.next/standalone/src/components/cortex/knowledge-card.tsx +109 -109
  713. package/.next/standalone/src/components/cortex/knowledge-tab.tsx +158 -158
  714. package/.next/standalone/src/components/cortex/lobe-settings.tsx +215 -215
  715. package/.next/standalone/src/components/cortex/marketplace-card.tsx +126 -126
  716. package/.next/standalone/src/components/cortex/marketplace-tab.tsx +113 -113
  717. package/.next/standalone/src/components/dashboard/activity-chart.tsx +41 -41
  718. package/.next/standalone/src/components/dashboard/model-usage-chart.tsx +61 -61
  719. package/.next/standalone/src/components/dashboard/recent-sessions.tsx +68 -68
  720. package/.next/standalone/src/components/dashboard/stats-cards.tsx +36 -36
  721. package/.next/standalone/src/components/files/file-explorer.tsx +703 -703
  722. package/.next/standalone/src/components/layout/providers.tsx +38 -38
  723. package/.next/standalone/src/components/layout/sidebar.tsx +170 -170
  724. package/.next/standalone/src/components/layout/tier-provider.tsx +53 -53
  725. package/.next/standalone/src/components/layout/update-banner.tsx +92 -92
  726. package/.next/standalone/src/components/mobile/bottom-nav.tsx +46 -46
  727. package/.next/standalone/src/components/mobile/immersive-voice-button.tsx +123 -123
  728. package/.next/standalone/src/components/mobile/mobile-chat-input.tsx +244 -244
  729. package/.next/standalone/src/components/mobile/mobile-header.tsx +44 -44
  730. package/.next/standalone/src/components/mobile/mobile-session-card.tsx +56 -56
  731. package/.next/standalone/src/components/mobile/mobile-terminal-input.tsx +74 -74
  732. package/.next/standalone/src/components/mobile/mobile-terminal-pane.tsx +302 -302
  733. package/.next/standalone/src/components/mobile/mobile-terminal-toolbar.tsx +76 -76
  734. package/.next/standalone/src/components/mobile/pull-to-refresh.tsx +82 -82
  735. package/.next/standalone/src/components/mobile/voice-input.tsx +53 -53
  736. package/.next/standalone/src/components/network/api-key-list.tsx +190 -190
  737. package/.next/standalone/src/components/network/connection-requests.tsx +94 -94
  738. package/.next/standalone/src/components/network/node-add-dialog.tsx +131 -131
  739. package/.next/standalone/src/components/network/node-badge.tsx +26 -26
  740. package/.next/standalone/src/components/network/node-list.tsx +207 -207
  741. package/.next/standalone/src/components/network/node-selector.tsx +49 -49
  742. package/.next/standalone/src/components/sessions/session-filters.tsx +116 -116
  743. package/.next/standalone/src/components/sessions/session-list.tsx +485 -485
  744. package/.next/standalone/src/components/terminal/pane-diff-panel.tsx +179 -179
  745. package/.next/standalone/src/components/terminal/terminal-pane.tsx +1530 -1464
  746. package/.next/standalone/src/components/viewer/chat-input.tsx +275 -275
  747. package/.next/standalone/src/components/viewer/message-renderer.tsx +551 -551
  748. package/.next/standalone/src/components/wizard/chart-wizard.tsx +405 -0
  749. package/.next/standalone/src/components/wizard/project-wizard.tsx +153 -153
  750. package/.next/standalone/src/components/wizard/wizard-chat.tsx +99 -99
  751. package/.next/standalone/src/components/wizard/wizard-plan-summary.tsx +103 -103
  752. package/.next/standalone/src/components/wizard/wizard-review.tsx +225 -225
  753. package/.next/standalone/src/components/workspace/universe-cluster.tsx +131 -131
  754. package/.next/standalone/src/components/workspace/universe-orb.tsx +128 -128
  755. package/.next/standalone/src/components/workspace/universe-types.ts +22 -22
  756. package/.next/standalone/src/components/workspace/universe-utils.ts +11 -11
  757. package/.next/standalone/src/components/workspace/universe-view.tsx +397 -397
  758. package/.next/standalone/src/components/workspace/workspace-chooser.tsx +634 -634
  759. package/.next/standalone/src/hooks/use-benchmark.ts +72 -71
  760. package/.next/standalone/src/hooks/use-bus.ts +147 -147
  761. package/.next/standalone/src/hooks/use-idle-detection.ts +79 -79
  762. package/.next/standalone/src/hooks/use-network.ts +229 -229
  763. package/.next/standalone/src/hooks/use-sessions.ts +437 -437
  764. package/.next/standalone/src/hooks/use-speech-recognition.ts +114 -113
  765. package/.next/standalone/src/hooks/use-sse.ts +35 -35
  766. package/.next/standalone/src/hooks/use-tier.ts +39 -39
  767. package/.next/standalone/src/lib/agents.ts +97 -97
  768. package/.next/standalone/src/lib/aider/parser.ts +111 -111
  769. package/.next/standalone/src/lib/api.ts +19 -19
  770. package/.next/standalone/src/lib/auth.ts +47 -47
  771. package/.next/standalone/src/lib/claude/parser.ts +212 -212
  772. package/.next/standalone/src/lib/claude/stats.ts +204 -204
  773. package/.next/standalone/src/lib/codex/parser.test.ts +111 -111
  774. package/.next/standalone/src/lib/codex/parser.ts +287 -287
  775. package/.next/standalone/src/lib/config.ts +132 -132
  776. package/.next/standalone/src/lib/cortex/benchmark.ts +83 -67
  777. package/.next/standalone/src/lib/cortex/config.ts +42 -42
  778. package/.next/standalone/src/lib/cortex/debug.ts +10 -10
  779. package/.next/standalone/src/lib/cortex/distillation/usage-store.ts +18 -18
  780. package/.next/standalone/src/lib/cortex/graph/resolver.ts +10 -10
  781. package/.next/standalone/src/lib/cortex/graph/types.ts +22 -22
  782. package/.next/standalone/src/lib/cortex/index.ts +109 -56
  783. package/.next/standalone/src/lib/cortex/ingestion/bootstrap.ts +14 -14
  784. package/.next/standalone/src/lib/cortex/knowledge/compat.ts +14 -14
  785. package/.next/standalone/src/lib/cortex/knowledge/contradiction.ts +10 -10
  786. package/.next/standalone/src/lib/cortex/knowledge/types.ts +67 -67
  787. package/.next/standalone/src/lib/cortex/lobes/config.ts +16 -16
  788. package/.next/standalone/src/lib/cortex/lobes/resolver.ts +8 -8
  789. package/.next/standalone/src/lib/cortex/lobes/shares.ts +14 -14
  790. package/.next/standalone/src/lib/cortex/mcp/server.ts +12 -12
  791. package/.next/standalone/src/lib/cortex/portability/exporter.ts +6 -6
  792. package/.next/standalone/src/lib/cortex/portability/importer.ts +10 -10
  793. package/.next/standalone/src/lib/cortex/retrieval/context-engine.ts +10 -10
  794. package/.next/standalone/src/lib/cortex/types.ts +39 -39
  795. package/.next/standalone/src/lib/cost-calculator.ts +48 -48
  796. package/.next/standalone/src/lib/db/init.ts +71 -71
  797. package/.next/standalone/src/lib/db/queries.ts +740 -827
  798. package/.next/standalone/src/lib/db/schema.ts +206 -206
  799. package/.next/standalone/src/lib/events/sse.ts +36 -36
  800. package/.next/standalone/src/lib/forge/parser.ts +52 -52
  801. package/.next/standalone/src/lib/gemini/parser.ts +258 -258
  802. package/.next/standalone/src/lib/license.ts +56 -56
  803. package/.next/standalone/src/lib/pro.ts +31 -31
  804. package/.next/standalone/src/lib/shell-user.ts +101 -0
  805. package/.next/standalone/src/lib/sync/indexer.ts +504 -504
  806. package/.next/standalone/src/lib/sync/watcher.ts +64 -64
  807. package/.next/standalone/src/lib/teams.ts +31 -31
  808. package/.next/standalone/src/lib/telemetry.ts +75 -75
  809. package/.next/standalone/src/lib/terminal/server.ts +188 -188
  810. package/.next/standalone/src/lib/tier.ts +38 -38
  811. package/.next/standalone/src/lib/utils.ts +72 -72
  812. package/.next/standalone/src/lib/vms/manager.ts +121 -121
  813. package/.next/standalone/src/middleware.ts +133 -133
  814. package/.next/standalone/src/types/claude.ts +208 -208
  815. package/.next/standalone/src/types/network.ts +61 -61
  816. package/.next/standalone/tests/setup.ts +8 -8
  817. package/.next/standalone/tsconfig.json +34 -34
  818. package/.next/standalone/vitest.config.ts +24 -24
  819. package/LICENSE +661 -661
  820. package/README.md +131 -131
  821. package/bin/cortex-hook.js +79 -79
  822. package/bin/cortex-hook.sh +62 -62
  823. package/bin/cortex-learn-hook.js +138 -138
  824. package/bin/cortex-mcp.js +60 -60
  825. package/bin/cortex-pi-extension.ts +170 -170
  826. package/bin/fix-standalone-externals.js +79 -79
  827. package/bin/lib/auto-setup.js +110 -110
  828. package/bin/mdns-service.js +171 -171
  829. package/bin/postinstall.js +35 -35
  830. package/bin/setup-admin.js +195 -195
  831. package/bin/spaces-dev.js +247 -247
  832. package/bin/spaces-install.js +660 -660
  833. package/bin/spaces-postinstall.js +50 -50
  834. package/bin/spaces-reset-totp.js +50 -50
  835. package/bin/spaces-service.js +1046 -1046
  836. package/bin/spaces-setup.js +253 -253
  837. package/bin/spaces.js +808 -805
  838. package/bin/ssh-auth-keys.sh +68 -68
  839. package/bin/terminal-server.js +2819 -2781
  840. package/package.json +111 -111
  841. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0903a426._.js +0 -98
  842. package/.next/standalone/.next/server/chunks/[root-of-the-server]__09e8ccc9._.js +0 -98
  843. package/.next/standalone/.next/server/chunks/[root-of-the-server]__11c684b1._.js +0 -98
  844. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1572d4ef._.js +0 -98
  845. package/.next/standalone/.next/server/chunks/[root-of-the-server]__186cd0bb._.js +0 -3
  846. package/.next/standalone/.next/server/chunks/[root-of-the-server]__212760e6._.js +0 -98
  847. package/.next/standalone/.next/server/chunks/[root-of-the-server]__228595ec._.js +0 -98
  848. package/.next/standalone/.next/server/chunks/[root-of-the-server]__283c890f._.js +0 -3
  849. package/.next/standalone/.next/server/chunks/[root-of-the-server]__2f300a68._.js +0 -98
  850. package/.next/standalone/.next/server/chunks/[root-of-the-server]__2f452778._.js +0 -98
  851. package/.next/standalone/.next/server/chunks/[root-of-the-server]__35f8e77e._.js +0 -98
  852. package/.next/standalone/.next/server/chunks/[root-of-the-server]__379fc2e9._.js +0 -98
  853. package/.next/standalone/.next/server/chunks/[root-of-the-server]__3b40d79f._.js +0 -98
  854. package/.next/standalone/.next/server/chunks/[root-of-the-server]__3d3dca2b._.js +0 -98
  855. package/.next/standalone/.next/server/chunks/[root-of-the-server]__4d5b78d2._.js +0 -98
  856. package/.next/standalone/.next/server/chunks/[root-of-the-server]__54163e52._.js +0 -98
  857. package/.next/standalone/.next/server/chunks/[root-of-the-server]__563c0817._.js +0 -3
  858. package/.next/standalone/.next/server/chunks/[root-of-the-server]__5812f90a._.js +0 -98
  859. package/.next/standalone/.next/server/chunks/[root-of-the-server]__5c5e87f5._.js +0 -98
  860. package/.next/standalone/.next/server/chunks/[root-of-the-server]__60d15b16._.js +0 -98
  861. package/.next/standalone/.next/server/chunks/[root-of-the-server]__69d315e5._.js +0 -3
  862. package/.next/standalone/.next/server/chunks/[root-of-the-server]__71f29038._.js +0 -98
  863. package/.next/standalone/.next/server/chunks/[root-of-the-server]__74084e07._.js +0 -3
  864. package/.next/standalone/.next/server/chunks/[root-of-the-server]__7921aa80._.js +0 -98
  865. package/.next/standalone/.next/server/chunks/[root-of-the-server]__7e077dd8._.js +0 -98
  866. package/.next/standalone/.next/server/chunks/[root-of-the-server]__7ebc4280._.js +0 -131
  867. package/.next/standalone/.next/server/chunks/[root-of-the-server]__857c60bb._.js +0 -98
  868. package/.next/standalone/.next/server/chunks/[root-of-the-server]__874fe565._.js +0 -98
  869. package/.next/standalone/.next/server/chunks/[root-of-the-server]__8e2171f7._.js +0 -98
  870. package/.next/standalone/.next/server/chunks/[root-of-the-server]__95659b2d._.js +0 -98
  871. package/.next/standalone/.next/server/chunks/[root-of-the-server]__9679b91e._.js +0 -98
  872. package/.next/standalone/.next/server/chunks/[root-of-the-server]__a90729a1._.js +0 -98
  873. package/.next/standalone/.next/server/chunks/[root-of-the-server]__ad4346fa._.js +0 -98
  874. package/.next/standalone/.next/server/chunks/[root-of-the-server]__b0862d69._.js +0 -98
  875. package/.next/standalone/.next/server/chunks/[root-of-the-server]__b43306ee._.js +0 -98
  876. package/.next/standalone/.next/server/chunks/[root-of-the-server]__b689ff5e._.js +0 -106
  877. package/.next/standalone/.next/server/chunks/[root-of-the-server]__ba87daaa._.js +0 -98
  878. package/.next/standalone/.next/server/chunks/[root-of-the-server]__c0461005._.js +0 -98
  879. package/.next/standalone/.next/server/chunks/[root-of-the-server]__c1deb5f3._.js +0 -98
  880. package/.next/standalone/.next/server/chunks/[root-of-the-server]__c8a62f42._.js +0 -98
  881. package/.next/standalone/.next/server/chunks/[root-of-the-server]__cabaac2b._.js +0 -98
  882. package/.next/standalone/.next/server/chunks/[root-of-the-server]__cb027619._.js +0 -98
  883. package/.next/standalone/.next/server/chunks/[root-of-the-server]__cf608218._.js +0 -98
  884. package/.next/standalone/.next/server/chunks/[root-of-the-server]__cfc1290d._.js +0 -98
  885. package/.next/standalone/.next/server/chunks/[root-of-the-server]__d0109b9b._.js +0 -98
  886. package/.next/standalone/.next/server/chunks/[root-of-the-server]__d0125483._.js +0 -3
  887. package/.next/standalone/.next/server/chunks/[root-of-the-server]__d048ee6b._.js +0 -98
  888. package/.next/standalone/.next/server/chunks/[root-of-the-server]__d12644e7._.js +0 -98
  889. package/.next/standalone/.next/server/chunks/[root-of-the-server]__e3cc946c._.js +0 -98
  890. package/.next/standalone/.next/server/chunks/[root-of-the-server]__e6fd27f8._.js +0 -98
  891. package/.next/standalone/.next/server/chunks/[root-of-the-server]__efb8251e._.js +0 -98
  892. package/.next/standalone/.next/server/chunks/[root-of-the-server]__f44c6882._.js +0 -98
  893. package/.next/standalone/.next/server/chunks/[root-of-the-server]__f85283de._.js +0 -98
  894. package/.next/standalone/.next/server/chunks/[root-of-the-server]__feceb3e4._.js +0 -98
  895. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__843070a6._.js +0 -3
  896. package/.next/standalone/.next/server/chunks/ssr/_e84a0c06._.js +0 -3
  897. package/.next/standalone/.next/static/chunks/470cade58d4eceeb.css +0 -3
  898. package/.next/standalone/.next/static/chunks/9d4164833c2c1fd6.js +0 -85
  899. package/.next/standalone/.next/static/chunks/f091f4bf8d80fd07.js +0 -1
  900. package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/README.md +0 -46
  901. package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/lib/glib-2.0/include/glibconfig.h +0 -221
  902. package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/lib/index.js +0 -1
  903. package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/lib/libvips-cpp.so.8.17.3 +0 -0
  904. package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/package.json +0 -42
  905. package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/README.md +0 -46
  906. package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/lib/glib-2.0/include/glibconfig.h +0 -221
  907. package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/lib/index.js +0 -1
  908. package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/lib/libvips-cpp.so.8.17.3 +0 -0
  909. package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/package.json +0 -42
  910. package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/versions.json +0 -30
  911. package/.next/standalone/node_modules/@img/sharp-linux-x64/lib/sharp-linux-x64.node +0 -0
  912. package/.next/standalone/node_modules/@img/sharp-linuxmusl-x64/lib/sharp-linuxmusl-x64.node +0 -0
  913. package/.next/standalone/node_modules/@img/sharp-linuxmusl-x64/package.json +0 -46
  914. /package/.next/standalone/.next/static/{5S4TviTCiNiTjf6KjXjBo → u1pHON3drz1mBi7owkbBP}/_buildManifest.js +0 -0
  915. /package/.next/standalone/.next/static/{5S4TviTCiNiTjf6KjXjBo → u1pHON3drz1mBi7owkbBP}/_clientMiddlewareManifest.json +0 -0
  916. /package/.next/standalone/.next/static/{5S4TviTCiNiTjf6KjXjBo → u1pHON3drz1mBi7owkbBP}/_ssgManifest.js +0 -0
  917. /package/.next/standalone/node_modules/@img/{sharp-libvips-linux-x64 → sharp-win32-x64}/versions.json +0 -0
@@ -1,1291 +1,1330 @@
1
- 'use client';
2
-
3
- import React, { useState, useEffect, useCallback, useRef } from 'react';
4
- import {
5
- Plus, Loader2, Terminal, Search, MessageSquare, FolderOpen,
6
- Clock, ChevronDown, ChevronRight, Save, FolderInput, Trash2, Pencil, Check, X,
7
- Layers, Copy, Home, XCircle, ArrowLeftToLine, Globe, AlertCircle, Users, Brain, Maximize2,
8
- } from 'lucide-react';
9
- import { useRouter } from 'next/navigation';
10
- import { TerminalPane } from '@/components/terminal/terminal-pane';
11
- import { DndContext, closestCenter, KeyboardSensor, PointerSensor, useSensor, useSensors, type DragEndEvent } from '@dnd-kit/core';
12
- import { SortableContext, useSortable, verticalListSortingStrategy, arrayMove } from '@dnd-kit/sortable';
13
- import { CSS } from '@dnd-kit/utilities';
14
- import { Group as PanelGroup, Panel, Separator as PanelResizeHandle } from 'react-resizable-panels';
15
- import { ColorPicker } from '@/components/common/color-picker';
16
- import { DevDirectoryPicker } from '@/components/common/dev-directory-picker';
17
- import { NodeSelector } from '@/components/network/node-selector';
18
- import { TotpGate } from '@/components/auth/totp-gate';
19
- import { AGENT_TYPES, AGENT_LIST } from '@/lib/agents';
20
- import type { PaneData } from '@/lib/db/queries';
21
- import type { SessionWithMeta, Workspace } from '@/types/claude';
22
- import { useRemoteWorkspaces } from '@/hooks/use-sessions';
23
- import { ActivityPanel } from '@/components/bus/activity-panel';
24
- import { useSSEBusEvents } from '@/hooks/use-sse';
25
- import { api } from '@/lib/api';
26
- import { track } from '@/lib/telemetry';
27
- import { LobeSettings } from '@/components/cortex/lobe-settings';
28
- import { FileExplorer } from '@/components/files/file-explorer';
29
- import { useTier } from '@/hooks/use-tier';
30
-
31
- function ResizablePaneGrid({ panes, renderPane }: {
32
- panes: PaneData[];
33
- renderPane: (pane: PaneData, dragHandleProps: Record<string, any>) => React.ReactNode;
34
- }) {
35
- if (panes.length === 0) return null;
36
-
37
- // Single pane — no resize handles needed
38
- if (panes.length === 1) {
39
- return (
40
- <div className="flex-1 p-1 overflow-hidden">
41
- <SortablePane id={panes[0].id}>
42
- {(dragHandleProps) => renderPane(panes[0], dragHandleProps)}
43
- </SortablePane>
44
- </div>
45
- );
46
- }
47
-
48
- // Split panes into rows: 2 per row for 2-4 panes, 3 per row for 5+
49
- const cols = panes.length <= 4 ? 2 : 3;
50
- const rows: PaneData[][] = [];
51
- for (let i = 0; i < panes.length; i += cols) {
52
- rows.push(panes.slice(i, i + cols));
53
- }
54
-
55
- return (
56
- <PanelGroup orientation="vertical" className="flex-1 p-1">
57
- {rows.map((row, rowIdx) => (
58
- <React.Fragment key={`row-${rowIdx}`}>
59
- {rowIdx > 0 && (
60
- <PanelResizeHandle className="h-1.5 flex items-center justify-center group cursor-row-resize">
61
- <div className="w-8 h-0.5 rounded-full bg-zinc-700 group-hover:bg-zinc-500 group-active:bg-indigo-500 transition-colors" />
62
- </PanelResizeHandle>
63
- )}
64
- <Panel minSize={15}>
65
- {row.length === 1 ? (
66
- <div className="h-full p-0.5">
67
- <SortablePane id={row[0].id}>
68
- {(dragHandleProps) => renderPane(row[0], dragHandleProps)}
69
- </SortablePane>
70
- </div>
71
- ) : (
72
- <PanelGroup orientation="horizontal" className="h-full">
73
- {row.map((pane, colIdx) => (
74
- <React.Fragment key={pane.id}>
75
- {colIdx > 0 && (
76
- <PanelResizeHandle className="w-1.5 flex items-center justify-center group cursor-col-resize">
77
- <div className="h-8 w-0.5 rounded-full bg-zinc-700 group-hover:bg-zinc-500 group-active:bg-indigo-500 transition-colors" />
78
- </PanelResizeHandle>
79
- )}
80
- <Panel minSize={15}>
81
- <div className="h-full p-0.5">
82
- <SortablePane id={pane.id}>
83
- {(dragHandleProps) => renderPane(pane, dragHandleProps)}
84
- </SortablePane>
85
- </div>
86
- </Panel>
87
- </React.Fragment>
88
- ))}
89
- </PanelGroup>
90
- )}
91
- </Panel>
92
- </React.Fragment>
93
- ))}
94
- </PanelGroup>
95
- );
96
- }
97
-
98
- function SortablePane({ id, children }: { id: string; children: (dragHandleProps: Record<string, any>) => React.ReactNode }) {
99
- const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id });
100
- const style = {
101
- transform: CSS.Transform.toString(transform),
102
- transition,
103
- opacity: isDragging ? 0.5 : 1,
104
- zIndex: isDragging ? 50 : 'auto' as any,
105
- };
106
- return (
107
- <div ref={setNodeRef} style={style} className="h-full">
108
- {children({ ...attributes, ...listeners })}
109
- </div>
110
- );
111
- }
112
-
113
- export default function TerminalPage() {
114
- return (
115
- <TotpGate>
116
- {(terminalToken) => <TerminalPageInner terminalToken={terminalToken} />}
117
- </TotpGate>
118
- );
119
- }
120
-
121
- function TerminalPageInner({ terminalToken }: { terminalToken: string }) {
122
- const router = useRouter();
123
- const { basePath, hasNetwork, hasCollaboration, hasCortex } = useTier();
124
- const [panes, setPanes] = useState<PaneData[]>([]);
125
- const [loading, setLoading] = useState(true);
126
- const [wsLoading, setWsLoading] = useState(true);
127
- const [showAdd, setShowAdd] = useState(false);
128
- const [maximized, setMaximized] = useState<string | null>(null);
129
- const [minimized, setMinimized] = useState<Set<string>>(new Set());
130
- const [poppedOut, setPoppedOut] = useState<Set<string>>(new Set());
131
- const [entered, setEntered] = useState(false);
132
- const [busPanelCollapsed, setBusPanelCollapsed] = useState(true);
133
-
134
- // SSE bus events for real-time updates
135
- useSSEBusEvents();
136
-
137
- // Workspace state
138
- const [workspaces, setWorkspaces] = useState<Workspace[]>([]);
139
- const [activeWorkspace, setActiveWorkspace] = useState<Workspace | null>(null);
140
- const [showWsPicker, setShowWsPicker] = useState(false);
141
- const [showSaveAs, setShowSaveAs] = useState(false);
142
- const [saveAsName, setSaveAsName] = useState('');
143
- const [saveAsColor, setSaveAsColor] = useState('#6366f1');
144
- const [editingWs, setEditingWs] = useState<number | null>(null);
145
- const [editWsName, setEditWsName] = useState('');
146
- const [lobeOpenWs, setLobeOpenWs] = useState<number | null>(null);
147
- const [showFiles, setShowFiles] = useState(false);
148
- const [browseTarget, setBrowseTarget] = useState<string | null>(null);
149
-
150
- const handleBrowse = useCallback((cwd: string) => {
151
- setBrowseTarget(cwd);
152
- setShowFiles(true);
153
- }, []);
154
-
155
- // New pane form state
156
- const [newTitle, setNewTitle] = useState('');
157
- const [newCwd, setNewCwd] = useState('');
158
- const [newColor, setNewColor] = useState('#6366f1');
159
- const [newClaudeSession, setNewClaudeSession] = useState('');
160
- const [newAgentType, setNewAgentType] = useState('claude');
161
- const [newAgentMode, setNewAgentMode] = useState<'new' | 'resume'>('new');
162
- const [newCustomCommand, setNewCustomCommand] = useState('');
163
- const [newNodeId, setNewNodeId] = useState('');
164
-
165
- // Remote workspaces
166
- const remoteWorkspacesQuery = useRemoteWorkspaces();
167
- const [collapsedNodes, setCollapsedNodes] = useState<Set<string>>(new Set());
168
- const toggleNodeCollapse = useCallback((nodeId: string) => {
169
- setCollapsedNodes(prev => {
170
- const next = new Set(prev);
171
- if (next.has(nodeId)) next.delete(nodeId); else next.add(nodeId);
172
- return next;
173
- });
174
- }, []);
175
-
176
- // Session picker state
177
- const [sessionSearch, setSessionSearch] = useState('');
178
- const [sessions, setSessions] = useState<SessionWithMeta[]>([]);
179
- const [sessionsLoading, setSessionsLoading] = useState(false);
180
- const [showSessionPicker, setShowSessionPicker] = useState(false);
181
- const [selectedSession, setSelectedSession] = useState<SessionWithMeta | null>(null);
182
- const [filterByCwd, setFilterByCwd] = useState(true);
183
- const pickerRef = useRef<HTMLDivElement>(null);
184
- const wsPickerRef = useRef<HTMLDivElement>(null);
185
- const channelRef = useRef<BroadcastChannel | null>(null);
186
-
187
- // ─── Data Loading ──────────────────────────────────────────
188
-
189
- const loadWorkspaces = useCallback(async () => {
190
- const res = await fetch(api('/api/workspaces'));
191
- const data = await res.json();
192
- setWorkspaces(data);
193
- const active = data.find((w: Workspace) => w.isActive);
194
- setActiveWorkspace(active || null);
195
- setWsLoading(false);
196
- return active || null;
197
- }, []);
198
-
199
- const loadPanes = useCallback(async () => {
200
- const res = await fetch(api('/api/panes'));
201
- const data = await res.json();
202
- setPanes(data);
203
- // Track which panes were popped out
204
- const popped = new Set<string>();
205
- for (const p of data) {
206
- if (p.isPopout) popped.add(p.id);
207
- }
208
- setPoppedOut(popped);
209
- setLoading(false);
210
- }, []);
211
-
212
- useEffect(() => {
213
- loadWorkspaces();
214
- }, [loadWorkspaces]);
215
-
216
- // Only load panes after entering a workspace
217
- useEffect(() => {
218
- if (entered) {
219
- loadPanes();
220
- }
221
- }, [entered, loadPanes]);
222
-
223
- // ─── BroadcastChannel for cross-window sync ────────────────
224
-
225
- useEffect(() => {
226
- const channel = new BroadcastChannel('spaces-panes');
227
- channelRef.current = channel;
228
-
229
- channel.onmessage = (event) => {
230
- const msg = event.data;
231
- if (msg.type === 'popout-opened') {
232
- setPoppedOut(prev => new Set(prev).add(msg.paneId));
233
- } else if (msg.type === 'popout-closed') {
234
- setPoppedOut(prev => {
235
- const next = new Set(prev);
236
- next.delete(msg.paneId);
237
- return next;
238
- });
239
- // Optimistically mark pane as not popped out in local state.
240
- // Don't call loadPanes() here — the sendBeacon that sets isPopout=false
241
- // in the DB is still in-flight, and loadPanes() would re-read the stale
242
- // isPopout=true value and put the pane right back into poppedOut.
243
- setPanes(prev => prev.map(p =>
244
- p.id === msg.paneId ? { ...p, isPopout: false } : p
245
- ));
246
- } else if (msg.type === 'pane-updated') {
247
- setPanes(prev => prev.map(p =>
248
- p.id === msg.paneId ? { ...p, ...msg.data } : p
249
- ));
250
- }
251
- };
252
-
253
- return () => channel.close();
254
- }, [loadPanes]);
255
-
256
- // Counter that increments on workspace switch to trigger popout restore
257
- const [restoreGen, setRestoreGen] = useState(0);
258
-
259
- // Auto-restore popped-out panes on load and after workspace switch
260
- useEffect(() => {
261
- if (loading) return;
262
- for (const pane of panes) {
263
- if (pane.isPopout) {
264
- openPopoutWindow(pane);
265
- }
266
- }
267
- // eslint-disable-next-line react-hooks/exhaustive-deps
268
- }, [loading, restoreGen]);
269
-
270
- // ─── Session Search ────────────────────────────────────────
271
-
272
- useEffect(() => {
273
- if (!(AGENT_TYPES[newAgentType]?.supportsResume && newAgentMode === 'resume')) return;
274
- setSessionsLoading(true);
275
- const timer = setTimeout(() => {
276
- const sp = new URLSearchParams({
277
- sortBy: 'modified', sortDir: 'DESC', limit: '200',
278
- });
279
- if (sessionSearch) sp.set('search', sessionSearch);
280
- if (filterByCwd && newCwd) sp.set('projectPath', newCwd);
281
- sp.set('agentType', newAgentType);
282
- fetch(api(`/api/sessions?${sp}`))
283
- .then(r => r.json())
284
- .then(data => { setSessions(data.sessions || []); setSessionsLoading(false); })
285
- .catch(() => setSessionsLoading(false));
286
- }, sessionSearch ? 300 : 0);
287
- return () => clearTimeout(timer);
288
- }, [newAgentType, newAgentMode, sessionSearch, filterByCwd, newCwd]);
289
-
290
- // Close pickers on outside click
291
- useEffect(() => {
292
- function handleClick(e: MouseEvent) {
293
- if (pickerRef.current && !pickerRef.current.contains(e.target as Node)) {
294
- setShowSessionPicker(false);
295
- }
296
- if (wsPickerRef.current && !wsPickerRef.current.contains(e.target as Node)) {
297
- setShowWsPicker(false);
298
- }
299
- }
300
- document.addEventListener('mousedown', handleClick);
301
- return () => document.removeEventListener('mousedown', handleClick);
302
- }, []);
303
-
304
- // ─── Pane Operations ──────────────────────────────────────
305
-
306
- const addPane = useCallback(async () => {
307
- const agent = AGENT_TYPES[newAgentType];
308
- const cwd = newCwd
309
- || (selectedSession?.projectPath)
310
- || '/';
311
-
312
- // For agent resume, pass the session ID; for new agent sessions, pass 'new'
313
- let claudeSessionId: string | undefined;
314
- if (newAgentType !== 'shell') {
315
- if (newAgentMode === 'resume' && agent?.supportsResume) {
316
- claudeSessionId = selectedSession?.sessionId || newClaudeSession;
317
- } else {
318
- claudeSessionId = 'new';
319
- }
320
- }
321
-
322
- const title = newTitle
323
- || (selectedSession ? (selectedSession.customName || selectedSession.firstPrompt?.slice(0, 50) || agent?.name || 'Agent') : '')
324
- || (newAgentType === 'shell' ? 'Terminal' : agent?.name || 'Agent');
325
-
326
- // Auto-opt-in to collaboration for agent panes when workspace has collaboration enabled
327
- const autoCollab = activeWorkspace?.collaboration && newAgentType !== 'shell';
328
-
329
- const res = await fetch(api('/api/panes'), {
330
- method: 'POST',
331
- headers: { 'Content-Type': 'application/json' },
332
- body: JSON.stringify({
333
- title, color: newColor, cwd, claudeSessionId,
334
- agentType: newAgentType,
335
- customCommand: newAgentType === 'custom' ? newCustomCommand : undefined,
336
- nodeId: newNodeId || undefined,
337
- isCollaborating: autoCollab || undefined,
338
- }),
339
- });
340
- const pane = await res.json();
341
- setPanes(prev => [...prev, pane]);
342
- track('pane_created', { agentType: newAgentType });
343
- setShowAdd(false);
344
- setNewTitle('');
345
- setNewCwd('');
346
- setNewColor('#6366f1');
347
- setNewClaudeSession('');
348
- setNewAgentType('shell');
349
- setNewAgentMode('new');
350
- setNewCustomCommand('');
351
- setNewNodeId('');
352
- setSelectedSession(null);
353
- setSessionSearch('');
354
- }, [newTitle, newCwd, newColor, newClaudeSession, newAgentType, newAgentMode, newCustomCommand, newNodeId, selectedSession]);
355
-
356
- const closePane = useCallback(async (id: string) => {
357
- const closing = panes.find(p => p.id === id);
358
- await fetch(api(`/api/panes/${id}`), { method: 'DELETE' });
359
- setPanes(prev => prev.filter(p => p.id !== id));
360
- if (closing) track('pane_closed', { agentType: closing.agentType });
361
- if (maximized === id) setMaximized(null);
362
- setPoppedOut(prev => {
363
- const next = new Set(prev);
364
- next.delete(id);
365
- return next;
366
- });
367
- }, [maximized, panes]);
368
-
369
- const updatePane = useCallback(async (id: string, data: Partial<PaneData>) => {
370
- await fetch(api(`/api/panes/${id}`), {
371
- method: 'PUT',
372
- headers: { 'Content-Type': 'application/json' },
373
- body: JSON.stringify(data),
374
- });
375
- setPanes(prev => prev.map(p => p.id === id ? { ...p, ...data } : p));
376
- }, []);
377
-
378
- const toggleMaximize = useCallback((id: string) => {
379
- setMaximized(prev => prev === id ? null : id);
380
- }, []);
381
-
382
- const minimizePane = useCallback((id: string) => {
383
- setMinimized(prev => new Set(prev).add(id));
384
- if (maximized === id) setMaximized(null);
385
- }, [maximized]);
386
-
387
- const restorePane = useCallback((id: string) => {
388
- setMinimized(prev => { const next = new Set(prev); next.delete(id); return next; });
389
- }, []);
390
-
391
- // ─── Drag to reorder ──────────────────────────────────────
392
- const sensors = useSensors(
393
- useSensor(PointerSensor, { activationConstraint: { distance: 8 } }),
394
- useSensor(KeyboardSensor),
395
- );
396
-
397
- const handleDragEnd = useCallback((event: DragEndEvent) => {
398
- const { active, over } = event;
399
- if (!over || active.id === over.id) return;
400
- setPanes(prev => {
401
- const oldIndex = prev.findIndex(p => p.id === active.id);
402
- const newIndex = prev.findIndex(p => p.id === over.id);
403
- if (oldIndex === -1 || newIndex === -1) return prev;
404
- const reordered = arrayMove(prev, oldIndex, newIndex);
405
- // Persist sort order
406
- reordered.forEach((p, i) => {
407
- if (p.sortOrder !== i) {
408
- fetch(api(`/api/panes/${p.id}`), {
409
- method: 'PUT',
410
- headers: { 'Content-Type': 'application/json' },
411
- body: JSON.stringify({ sortOrder: i }),
412
- }).catch(() => {});
413
- }
414
- });
415
- return reordered.map((p, i) => ({ ...p, sortOrder: i }));
416
- });
417
- }, []);
418
-
419
- // ─── Popout ────────────────────────────────────────────────
420
-
421
- const openPopoutWindow = useCallback((pane: PaneData) => {
422
- const w = pane.winWidth || 900;
423
- const h = pane.winHeight || 600;
424
- const x = pane.winX ?? Math.round(screen.width / 2 - w / 2);
425
- const y = pane.winY ?? Math.round(screen.height / 2 - h / 2);
426
- const features = `left=${x},top=${y},width=${w},height=${h},menubar=no,toolbar=no,location=no,status=no`;
427
-
428
- window.open(api(`/terminal/pane/${pane.id}`), `spaces-pane-${pane.id}`, features);
429
- setPoppedOut(prev => new Set(prev).add(pane.id));
430
-
431
- // Mark in DB
432
- fetch(api(`/api/panes/${pane.id}`), {
433
- method: 'PUT',
434
- headers: { 'Content-Type': 'application/json' },
435
- body: JSON.stringify({ isPopout: true, winX: x, winY: y, winWidth: w, winHeight: h }),
436
- });
437
- }, []);
438
-
439
- const handlePopout = useCallback((id: string) => {
440
- const pane = panes.find(p => p.id === id);
441
- if (pane) openPopoutWindow(pane);
442
- }, [panes, openPopoutWindow]);
443
-
444
- // ─── Pop in (return popout to grid) ──────────────────────
445
-
446
- const popIn = useCallback(async (id: string) => {
447
- // Tell the popout window to close itself
448
- channelRef.current?.postMessage({ type: 'close-popouts' });
449
- // Mark as not popped out in DB
450
- await fetch(api(`/api/panes/${id}`), {
451
- method: 'PUT',
452
- headers: { 'Content-Type': 'application/json' },
453
- body: JSON.stringify({ isPopout: false }),
454
- });
455
- setPoppedOut(prev => {
456
- const next = new Set(prev);
457
- next.delete(id);
458
- return next;
459
- });
460
- }, []);
461
-
462
- // ─── Close all popout windows ─────────────────────────────
463
-
464
- const closeAllPopouts = useCallback(() => {
465
- if (poppedOut.size > 0) {
466
- channelRef.current?.postMessage({ type: 'close-popouts' });
467
- setPoppedOut(new Set());
468
- }
469
- }, [poppedOut]);
470
-
471
- // ─── Workspace Operations ──────────────────────────────────
472
-
473
- const switchWorkspace = useCallback(async (wsId: number) => {
474
- // Close all popout windows first (saves their positions)
475
- closeAllPopouts();
476
- await fetch(api('/api/workspaces'), {
477
- method: 'POST',
478
- headers: { 'Content-Type': 'application/json' },
479
- body: JSON.stringify({ action: 'switch', workspaceId: wsId }),
480
- });
481
- setMaximized(null);
482
- await loadWorkspaces();
483
- await loadPanes();
484
- setShowWsPicker(false);
485
- if (entered) {
486
- track('workspace_switched');
487
- } else {
488
- track('workspace_entered');
489
- }
490
- setEntered(true);
491
- // Trigger popout restore for the new workspace's panes
492
- setRestoreGen(prev => prev + 1);
493
- }, [loadWorkspaces, loadPanes, closeAllPopouts, entered]);
494
-
495
- const saveWorkspaceAs = useCallback(async () => {
496
- if (!saveAsName.trim() || !activeWorkspace) return;
497
- await fetch(api('/api/workspaces'), {
498
- method: 'POST',
499
- headers: { 'Content-Type': 'application/json' },
500
- body: JSON.stringify({ action: 'duplicate', sourceId: activeWorkspace.id, name: saveAsName.trim(), color: saveAsColor }),
501
- });
502
- setSaveAsName('');
503
- setSaveAsColor('#6366f1');
504
- setShowSaveAs(false);
505
- await loadWorkspaces();
506
- }, [saveAsName, saveAsColor, activeWorkspace, loadWorkspaces]);
507
-
508
- const createNewWorkspace = useCallback(async () => {
509
- const res = await fetch(api('/api/workspaces'), {
510
- method: 'POST',
511
- headers: { 'Content-Type': 'application/json' },
512
- body: JSON.stringify({ name: 'New Space', color: '#6366f1' }),
513
- });
514
- const ws = await res.json();
515
- track('workspace_created');
516
- await switchWorkspace(ws.id);
517
- }, [switchWorkspace]);
518
-
519
- const deleteWorkspace = useCallback(async (wsId: number) => {
520
- closeAllPopouts();
521
- await fetch(api(`/api/workspaces/${wsId}`), { method: 'DELETE' });
522
- track('workspace_deleted');
523
- await loadWorkspaces();
524
- await loadPanes();
525
- setShowWsPicker(false);
526
- }, [loadWorkspaces, loadPanes, closeAllPopouts]);
527
-
528
- const closeWorkspace = useCallback(() => {
529
- closeAllPopouts();
530
- setPanes([]);
531
- setEntered(false);
532
- }, [closeAllPopouts]);
533
-
534
- const renameWorkspace = useCallback(async (wsId: number, name: string) => {
535
- await fetch(api(`/api/workspaces/${wsId}`), {
536
- method: 'PUT',
537
- headers: { 'Content-Type': 'application/json' },
538
- body: JSON.stringify({ name }),
539
- });
540
- setEditingWs(null);
541
- await loadWorkspaces();
542
- }, [loadWorkspaces]);
543
-
544
- // ─── Render ────────────────────────────────────────────────
545
-
546
- const visiblePanes = panes.filter(p => !poppedOut.has(p.id) && !minimized.has(p.id));
547
- const minimizedPanes = panes.filter(p => minimized.has(p.id));
548
-
549
- // ─── Workspace chooser (before entering) ─────────────────
550
-
551
- if (!entered) {
552
- return (
553
- <div className="h-screen flex flex-col bg-zinc-950 text-zinc-100">
554
- <div className="absolute top-4 left-4">
555
- <button
556
- onClick={() => router.push('/')}
557
- className="flex items-center gap-2 px-3 py-1.5 text-xs text-zinc-500 hover:text-white hover:bg-zinc-800 rounded-md transition-colors"
558
- >
559
- <Home className="w-3.5 h-3.5" />
560
- Home
561
- </button>
562
- </div>
563
- <div className="flex-1 flex items-center justify-center p-8 overflow-y-auto min-h-0">
564
- <div className="w-full max-w-2xl my-auto">
565
- <div className="text-center mb-10">
566
- {/* eslint-disable-next-line @next/next/no-img-element */}
567
- <img src={`${basePath}/spaces_icon.png`} alt="Spaces" className="w-16 h-16 mx-auto mb-4" />
568
- <h1 className="text-2xl font-bold mb-2">Spaces</h1>
569
- <p className="text-zinc-400 text-sm">Choose a workspace to open, or create a new one.</p>
570
- </div>
571
-
572
- {wsLoading ? (
573
- <div className="flex justify-center py-12">
574
- <Loader2 className="w-6 h-6 animate-spin text-indigo-500" />
575
- </div>
576
- ) : (
577
- <>
578
- <div className="grid grid-cols-2 gap-3 mb-6">
579
- {workspaces.map((ws) => (
580
- <div
581
- key={ws.id}
582
- className={`relative bg-zinc-900 border rounded-lg transition-all ${
583
- lobeOpenWs === ws.id ? 'border-zinc-600' : 'border-zinc-800 hover:border-zinc-600 hover:bg-zinc-800/50'
584
- }`}
585
- >
586
- <button
587
- onClick={() => switchWorkspace(ws.id)}
588
- className="flex items-center gap-3 p-4 text-left w-full group"
589
- >
590
- <span
591
- className="w-3.5 h-3.5 rounded-full flex-shrink-0"
592
- style={{ backgroundColor: ws.color }}
593
- />
594
- <div className="flex-1 min-w-0">
595
- <div className="text-sm font-medium text-white truncate">{ws.name}</div>
596
- <div className="text-xs text-zinc-500 mt-0.5">
597
- {ws.paneCount || 0} pane{(ws.paneCount || 0) !== 1 ? 's' : ''}
598
- {ws.isActive && <span className="text-indigo-400 ml-2">last active</span>}
599
- </div>
600
- </div>
601
- <Terminal className="w-4 h-4 text-zinc-600 group-hover:text-zinc-400 transition-colors" />
602
- </button>
603
- {hasCortex && (
604
- <button
605
- onClick={() => setLobeOpenWs(lobeOpenWs === ws.id ? null : ws.id)}
606
- className={`absolute top-2 right-2 p-1 rounded transition-colors ${
607
- lobeOpenWs === ws.id ? 'text-purple-400 bg-purple-500/10' : 'text-zinc-700 hover:text-zinc-400'
608
- }`}
609
- title="Knowledge lobe settings"
610
- >
611
- <Brain className="w-3.5 h-3.5" />
612
- </button>
613
- )}
614
- {lobeOpenWs === ws.id && (
615
- <div className="px-4 pb-4 border-t border-zinc-800/50 pt-3">
616
- <LobeSettings workspaceId={ws.id} workspaceName={ws.name} />
617
- </div>
618
- )}
619
- </div>
620
- ))}
621
- </div>
622
-
623
- <div className="flex justify-center">
624
- <button
625
- onClick={createNewWorkspace}
626
- className="flex items-center gap-2 px-4 py-2.5 text-sm border border-zinc-700 text-zinc-400 rounded-lg hover:text-white hover:border-zinc-500 transition-colors"
627
- >
628
- <Plus className="w-4 h-4" />
629
- New Workspace
630
- </button>
631
- </div>
632
-
633
- {/* Remote workspaces from network nodes */}
634
- {hasNetwork && remoteWorkspacesQuery.isLoading && (
635
- <div className="flex items-center justify-center gap-2 py-4 mt-6 border-t border-zinc-800">
636
- <Loader2 className="w-4 h-4 animate-spin text-zinc-500" />
637
- <span className="text-xs text-zinc-500">Loading network spaces...</span>
638
- </div>
639
- )}
640
-
641
- {hasNetwork && remoteWorkspacesQuery.data?.remote && remoteWorkspacesQuery.data.remote.length > 0 && (
642
- <div className="mt-6 border-t border-zinc-800 pt-6">
643
- <div className="flex items-center gap-2 mb-4">
644
- <Globe className="w-4 h-4 text-zinc-500" />
645
- <h2 className="text-sm font-medium text-zinc-400">Network Spaces</h2>
646
- </div>
647
-
648
- {remoteWorkspacesQuery.data.remote.map((node) => (
649
- <div key={node.nodeId} className="mb-3">
650
- <button
651
- onClick={() => toggleNodeCollapse(node.nodeId)}
652
- className="flex items-center gap-2 w-full px-2 py-1.5 text-left text-xs text-zinc-400 hover:text-white rounded-md hover:bg-zinc-800/50 transition-colors"
653
- >
654
- {collapsedNodes.has(node.nodeId) ? (
655
- <ChevronRight className="w-3.5 h-3.5 text-zinc-600" />
656
- ) : (
657
- <ChevronDown className="w-3.5 h-3.5 text-zinc-600" />
658
- )}
659
- <Globe className="w-3 h-3 text-zinc-600" />
660
- <span className="font-medium">{node.nodeName}</span>
661
- <span className="text-zinc-600">
662
- {node.workspaces.length} space{node.workspaces.length !== 1 ? 's' : ''}
663
- </span>
664
- </button>
665
-
666
- {!collapsedNodes.has(node.nodeId) && (
667
- <div className="grid grid-cols-2 gap-2 mt-2 ml-5">
668
- {node.workspaces.map((ws) => (
669
- <button
670
- key={`${node.nodeId}-${ws.id}`}
671
- onClick={() => {
672
- router.push(`/terminal/remote/${node.nodeId}/${ws.id}`);
673
- }}
674
- className="flex items-center gap-3 p-3 bg-zinc-900/50 border border-zinc-800/50 rounded-lg hover:border-zinc-600 hover:bg-zinc-800/30 transition-colors text-left group"
675
- >
676
- <span
677
- className="w-3 h-3 rounded-full flex-shrink-0 ring-1 ring-zinc-700"
678
- style={{ backgroundColor: ws.color }}
679
- />
680
- <div className="flex-1 min-w-0">
681
- <div className="text-xs font-medium text-zinc-300 truncate">{ws.name}</div>
682
- <div className="text-[10px] text-zinc-600 mt-0.5">
683
- {ws.paneCount || 0} pane{(ws.paneCount || 0) !== 1 ? 's' : ''}
684
- </div>
685
- </div>
686
- <Globe className="w-3 h-3 text-zinc-700 group-hover:text-zinc-500 transition-colors" />
687
- </button>
688
- ))}
689
- </div>
690
- )}
691
- </div>
692
- ))}
693
-
694
- {/* Error indicators for nodes that failed */}
695
- {remoteWorkspacesQuery.data.errors && remoteWorkspacesQuery.data.errors.length > 0 && (
696
- <div className="mt-3 space-y-1">
697
- {remoteWorkspacesQuery.data.errors.map((err) => (
698
- <div key={err.nodeId} className="flex items-center gap-2 px-2 py-1.5 text-[10px] text-zinc-600">
699
- <AlertCircle className="w-3 h-3 text-red-500/50" />
700
- <span>{err.nodeName}: {err.error}</span>
701
- </div>
702
- ))}
703
- </div>
704
- )}
705
- </div>
706
- )}
707
- </>
708
- )}
709
- </div>
710
- </div>
711
- </div>
712
- );
713
- }
714
-
715
- if (loading) {
716
- return (
717
- <div className="flex items-center justify-center h-screen bg-zinc-950">
718
- <Loader2 className="w-6 h-6 animate-spin text-indigo-500" />
719
- </div>
720
- );
721
- }
722
-
723
- return (
724
- <div className="h-screen flex flex-col bg-zinc-950 text-zinc-100">
725
- {/* Header */}
726
- <div className="flex items-center justify-between px-4 py-2 border-b border-zinc-800 flex-shrink-0">
727
- <div className="flex items-center gap-3">
728
- <button
729
- onClick={closeWorkspace}
730
- className="p-1.5 -ml-1 text-zinc-500 hover:text-white hover:bg-zinc-800 rounded-md transition-colors"
731
- title="Close space and go home"
732
- >
733
- <Home className="w-4 h-4" />
734
- </button>
735
- <div className="w-px h-4 bg-zinc-800" />
736
- <h1 className="text-sm font-semibold">Spaces</h1>
737
-
738
- {/* Workspace selector */}
739
- <div ref={wsPickerRef} className="relative">
740
- <button
741
- onClick={() => setShowWsPicker(!showWsPicker)}
742
- className="flex items-center gap-1.5 px-2.5 py-1 text-xs bg-zinc-800 border border-zinc-700 rounded-md hover:border-zinc-600 transition-colors"
743
- >
744
- {activeWorkspace && (
745
- <span className="w-2 h-2 rounded-full flex-shrink-0" style={{ backgroundColor: activeWorkspace.color }} />
746
- )}
747
- <span className="max-w-[150px] truncate">{activeWorkspace?.name || 'No space'}</span>
748
- <ChevronDown className="w-3 h-3 text-zinc-500" />
749
- </button>
750
-
751
- {showWsPicker && (
752
- <div className="absolute z-50 top-full left-0 mt-1 w-72 bg-zinc-800 border border-zinc-700 rounded-md shadow-xl overflow-hidden">
753
- <div className="p-2 border-b border-zinc-700/50 text-[10px] text-zinc-500 uppercase tracking-wider font-medium">
754
- Switch Space
755
- </div>
756
- <div className="max-h-[300px] overflow-y-auto">
757
- {workspaces.map((ws) => (
758
- <div key={ws.id} className="flex items-center gap-2 hover:bg-zinc-700/50 transition-colors group">
759
- {editingWs === ws.id ? (
760
- <div className="flex items-center gap-1 flex-1 px-3 py-2">
761
- <input
762
- autoFocus
763
- value={editWsName}
764
- onChange={(e) => setEditWsName(e.target.value)}
765
- onKeyDown={(e) => {
766
- if (e.key === 'Enter') renameWorkspace(ws.id, editWsName);
767
- if (e.key === 'Escape') setEditingWs(null);
768
- }}
769
- className="flex-1 bg-transparent border border-zinc-600 rounded px-1.5 py-0.5 text-xs focus:outline-none focus:border-indigo-400"
770
- />
771
- <button onClick={() => renameWorkspace(ws.id, editWsName)} className="text-green-400 hover:text-green-300">
772
- <Check className="w-3 h-3" />
773
- </button>
774
- </div>
775
- ) : (
776
- <>
777
- <button
778
- onClick={() => switchWorkspace(ws.id)}
779
- className="flex items-center gap-2 flex-1 px-3 py-2 text-left"
780
- >
781
- <span className="w-2.5 h-2.5 rounded-full flex-shrink-0" style={{ backgroundColor: ws.color }} />
782
- <span className="text-xs text-white truncate flex-1">{ws.name}</span>
783
- <span className="text-[10px] text-zinc-500">{ws.paneCount || 0} panes</span>
784
- {ws.isActive && <span className="text-[10px] text-indigo-400">active</span>}
785
- </button>
786
- <div className="flex items-center gap-0.5 pr-2 opacity-0 group-hover:opacity-100">
787
- <button
788
- onClick={(e) => { e.stopPropagation(); setEditingWs(ws.id); setEditWsName(ws.name); }}
789
- className="p-1 text-zinc-500 hover:text-white rounded"
790
- title="Rename"
791
- >
792
- <Pencil className="w-2.5 h-2.5" />
793
- </button>
794
- {!ws.isActive && workspaces.length > 1 && (
795
- <button
796
- onClick={(e) => {
797
- e.stopPropagation();
798
- if (confirm(`Delete space "${ws.name}" and all its panes?`)) {
799
- deleteWorkspace(ws.id);
800
- }
801
- }}
802
- className="p-1 text-zinc-500 hover:text-red-400 rounded"
803
- title="Delete"
804
- >
805
- <Trash2 className="w-2.5 h-2.5" />
806
- </button>
807
- )}
808
- </div>
809
- </>
810
- )}
811
- </div>
812
- ))}
813
- </div>
814
-
815
- <div className="border-t border-zinc-700/50 p-1.5 flex gap-1">
816
- <button
817
- onClick={createNewWorkspace}
818
- className="flex items-center gap-1.5 px-2.5 py-1.5 text-[11px] text-zinc-400 hover:text-white hover:bg-zinc-700 rounded transition-colors flex-1"
819
- >
820
- <Plus className="w-3 h-3" /> New Empty
821
- </button>
822
- <button
823
- onClick={() => { setShowSaveAs(true); setShowWsPicker(false); }}
824
- className="flex items-center gap-1.5 px-2.5 py-1.5 text-[11px] text-zinc-400 hover:text-white hover:bg-zinc-700 rounded transition-colors flex-1"
825
- >
826
- <Copy className="w-3 h-3" /> Duplicate Current
827
- </button>
828
- </div>
829
- </div>
830
- )}
831
- </div>
832
-
833
- <span className="text-[11px] text-zinc-500">
834
- {visiblePanes.length} pane{visiblePanes.length !== 1 ? 's' : ''}
835
- {poppedOut.size > 0 && ` + ${poppedOut.size} popped out`}
836
- </span>
837
- </div>
838
-
839
- <div className="flex items-center gap-2">
840
- <button
841
- onClick={() => setShowFiles(!showFiles)}
842
- className={`flex items-center gap-1.5 px-2.5 py-1.5 text-xs rounded-md border transition-colors ${
843
- showFiles
844
- ? 'border-amber-500/50 bg-amber-600/20 text-amber-400'
845
- : 'border-zinc-700 text-zinc-500 hover:text-zinc-300 hover:border-zinc-600'
846
- }`}
847
- title={showFiles ? 'Hide file explorer' : 'Show file explorer'}
848
- >
849
- <FolderOpen className="w-3.5 h-3.5" />
850
- Files
851
- </button>
852
- {hasCollaboration && activeWorkspace && (
853
- <button
854
- onClick={async () => {
855
- const newVal = !activeWorkspace.collaboration;
856
- await fetch(api(`/api/workspaces/${activeWorkspace.id}`), {
857
- method: 'PUT',
858
- headers: { 'Content-Type': 'application/json' },
859
- body: JSON.stringify({ collaboration: newVal }),
860
- });
861
- setActiveWorkspace(prev => prev ? { ...prev, collaboration: newVal } : prev);
862
- setWorkspaces(prev => prev.map(w => w.id === activeWorkspace.id ? { ...w, collaboration: newVal } : w));
863
-
864
- // Sync all agent panes in this workspace to match the new collaboration state
865
- const agentPanes = panes.filter(p => p.workspaceId === activeWorkspace.id && p.agentType !== 'shell');
866
- for (const p of agentPanes) {
867
- if (p.isCollaborating !== newVal) {
868
- await updatePane(p.id, { isCollaborating: newVal } as Partial<PaneData>);
869
- }
870
- }
871
- }}
872
- className={`flex items-center gap-1.5 px-2.5 py-1.5 text-xs rounded-md border transition-colors ${
873
- activeWorkspace.collaboration
874
- ? 'border-indigo-500/50 bg-indigo-600/20 text-indigo-400'
875
- : 'border-zinc-700 text-zinc-500 hover:text-zinc-300 hover:border-zinc-600'
876
- }`}
877
- title={activeWorkspace.collaboration ? 'Collaboration enabled — click to disable' : 'Enable collaboration for this workspace'}
878
- >
879
- <Users className="w-3.5 h-3.5" />
880
- Collab
881
- </button>
882
- )}
883
- <button
884
- onClick={closeWorkspace}
885
- className="flex items-center gap-1.5 px-3 py-1.5 text-xs border border-zinc-700 text-zinc-400 rounded-md hover:text-white hover:border-zinc-600 transition-colors"
886
- title="Close space and return home"
887
- >
888
- <XCircle className="w-3.5 h-3.5" />
889
- Close
890
- </button>
891
- <button
892
- onClick={() => setShowAdd(true)}
893
- className="flex items-center gap-1.5 px-3 py-1.5 text-xs bg-indigo-600 text-white rounded-md hover:bg-indigo-500 transition-colors"
894
- >
895
- <Plus className="w-3.5 h-3.5" />
896
- Add Pane
897
- </button>
898
- </div>
899
- </div>
900
-
901
- {/* Save-as dialog */}
902
- {showSaveAs && (
903
- <div className="border-b border-zinc-800 bg-zinc-900 px-4 py-3">
904
- <div className="max-w-md flex items-center gap-2">
905
- <Save className="w-4 h-4 text-indigo-400 flex-shrink-0" />
906
- <input
907
- autoFocus
908
- type="text"
909
- placeholder="New space name..."
910
- value={saveAsName}
911
- onChange={(e) => setSaveAsName(e.target.value)}
912
- onKeyDown={(e) => e.key === 'Enter' && saveWorkspaceAs()}
913
- className="flex-1 px-3 py-1.5 text-sm bg-zinc-800 border border-zinc-700 rounded-md focus:outline-none focus:border-indigo-500 text-white"
914
- />
915
- <ColorPicker value={saveAsColor} onChange={setSaveAsColor} />
916
- <button
917
- onClick={saveWorkspaceAs}
918
- disabled={!saveAsName.trim()}
919
- className="px-3 py-1.5 text-xs bg-indigo-600 text-white rounded-md hover:bg-indigo-500 disabled:opacity-50"
920
- >
921
- Save
922
- </button>
923
- <button
924
- onClick={() => setShowSaveAs(false)}
925
- className="p-1.5 text-zinc-400 hover:text-white"
926
- >
927
- <X className="w-4 h-4" />
928
- </button>
929
- </div>
930
- </div>
931
- )}
932
-
933
- {/* Add pane dialog */}
934
- {showAdd && (
935
- <div className="border-b border-zinc-800 bg-zinc-900 px-4 py-4">
936
- <div className="max-w-lg space-y-3">
937
- <h3 className="text-sm font-semibold">Add Pane</h3>
938
-
939
- {/* Agent type grid */}
940
- <div>
941
- <label className="text-[11px] text-zinc-400 mb-1.5 block">Agent</label>
942
- <div className="grid grid-cols-3 gap-1.5">
943
- {AGENT_LIST.map((agent) => (
944
- <button
945
- key={agent.id}
946
- onClick={() => {
947
- setNewAgentType(agent.id);
948
- setNewAgentMode('new');
949
- setSelectedSession(null);
950
- setNewClaudeSession('');
951
- }}
952
- className={`flex items-center gap-2 px-3 py-2 rounded-md border text-left transition-colors ${
953
- newAgentType === agent.id
954
- ? 'border-indigo-500 bg-indigo-600/20 text-white'
955
- : 'border-zinc-700 text-zinc-400 hover:text-white hover:border-zinc-600'
956
- }`}
957
- >
958
- <span
959
- className="w-2.5 h-2.5 rounded-full flex-shrink-0"
960
- style={{ backgroundColor: agent.color }}
961
- />
962
- <div className="min-w-0">
963
- <div className="text-xs font-medium truncate">{agent.name}</div>
964
- <div className="text-[10px] text-zinc-500 truncate">{agent.description}</div>
965
- </div>
966
- </button>
967
- ))}
968
- </div>
969
- </div>
970
-
971
- {/* New / Resume toggle (only for agents that support resume) */}
972
- {AGENT_TYPES[newAgentType]?.supportsResume && (
973
- <div className="flex gap-2">
974
- <button
975
- onClick={() => { setNewAgentMode('new'); setSelectedSession(null); setNewClaudeSession(''); }}
976
- className={`px-3 py-1.5 text-xs rounded-md border transition-colors ${
977
- newAgentMode === 'new'
978
- ? 'bg-indigo-600 border-indigo-500 text-white'
979
- : 'border-zinc-700 text-zinc-400 hover:text-white hover:border-zinc-600'
980
- }`}
981
- >
982
- New Session
983
- </button>
984
- <button
985
- onClick={() => setNewAgentMode('resume')}
986
- className={`px-3 py-1.5 text-xs rounded-md border transition-colors ${
987
- newAgentMode === 'resume'
988
- ? 'bg-indigo-600 border-indigo-500 text-white'
989
- : 'border-zinc-700 text-zinc-400 hover:text-white hover:border-zinc-600'
990
- }`}
991
- >
992
- Resume Session
993
- </button>
994
- </div>
995
- )}
996
-
997
- {/* Custom command input */}
998
- {newAgentType === 'custom' && (
999
- <input
1000
- type="text"
1001
- placeholder="Command to run (e.g. python agent.py)"
1002
- value={newCustomCommand}
1003
- onChange={(e) => setNewCustomCommand(e.target.value)}
1004
- className="w-full px-3 py-2 text-sm bg-zinc-800 border border-zinc-700 rounded-md focus:outline-none focus:border-indigo-500 text-white font-mono"
1005
- />
1006
- )}
1007
-
1008
- <NodeSelector value={newNodeId} onChange={setNewNodeId} />
1009
-
1010
- <input
1011
- type="text"
1012
- placeholder="Title (optional)"
1013
- value={newTitle}
1014
- onChange={(e) => setNewTitle(e.target.value)}
1015
- className="w-full px-3 py-2 text-sm bg-zinc-800 border border-zinc-700 rounded-md focus:outline-none focus:border-indigo-500 text-white"
1016
- />
1017
-
1018
- {newNodeId ? (
1019
- <div>
1020
- <label className="text-[11px] text-zinc-400 mb-1.5 block">Working directory (on remote node)</label>
1021
- <input
1022
- type="text"
1023
- placeholder="e.g. /home/user/project or ~ for home"
1024
- value={newCwd}
1025
- onChange={(e) => setNewCwd(e.target.value)}
1026
- className="w-full px-3 py-2 text-sm bg-zinc-800 border border-zinc-700 rounded-md focus:outline-none focus:border-indigo-500 text-white font-mono"
1027
- />
1028
- </div>
1029
- ) : (
1030
- <DevDirectoryPicker value={newCwd} onChange={setNewCwd} />
1031
- )}
1032
-
1033
- {/* Session picker for resume mode */}
1034
- {AGENT_TYPES[newAgentType]?.supportsResume && newAgentMode === 'resume' && (
1035
- <div ref={pickerRef} className="relative">
1036
- {selectedSession ? (
1037
- <div className="flex items-center gap-2 px-3 py-2 text-sm bg-zinc-800 border border-zinc-700 rounded-md">
1038
- <MessageSquare className="w-3.5 h-3.5 text-indigo-400 flex-shrink-0" />
1039
- <div className="flex-1 min-w-0">
1040
- <div className="text-white truncate">
1041
- {selectedSession.customName || selectedSession.firstPrompt?.slice(0, 60) || 'Untitled'}
1042
- </div>
1043
- <div className="text-[10px] text-zinc-500 flex items-center gap-2">
1044
- <span>{selectedSession.projectName}</span>
1045
- <span>{selectedSession.messageCount} msgs</span>
1046
- <span className="font-mono">{selectedSession.sessionId.slice(0, 8)}...</span>
1047
- </div>
1048
- </div>
1049
- <button
1050
- onClick={() => { setSelectedSession(null); setShowSessionPicker(true); }}
1051
- className="text-zinc-400 hover:text-white text-xs"
1052
- >
1053
- Change
1054
- </button>
1055
- </div>
1056
- ) : (
1057
- <div
1058
- className="flex items-center gap-2 px-3 py-2 text-sm bg-zinc-800 border border-zinc-700 rounded-md cursor-text"
1059
- onClick={() => setShowSessionPicker(true)}
1060
- >
1061
- <Search className="w-3.5 h-3.5 text-zinc-500" />
1062
- <input
1063
- type="text"
1064
- placeholder="Search sessions by name, prompt, or project..."
1065
- value={sessionSearch}
1066
- onChange={(e) => { setSessionSearch(e.target.value); setShowSessionPicker(true); }}
1067
- onFocus={() => setShowSessionPicker(true)}
1068
- className="flex-1 bg-transparent focus:outline-none text-white placeholder:text-zinc-500"
1069
- />
1070
- {newCwd && (
1071
- <button
1072
- onClick={(e) => { e.stopPropagation(); setFilterByCwd(!filterByCwd); }}
1073
- className={`flex-shrink-0 px-1.5 py-0.5 text-[10px] rounded border transition-colors ${
1074
- filterByCwd
1075
- ? 'border-indigo-500/50 bg-indigo-600/20 text-indigo-400'
1076
- : 'border-zinc-600 text-zinc-500 hover:text-zinc-400'
1077
- }`}
1078
- title={filterByCwd ? 'Showing sessions for this directory — click to show all' : 'Showing all sessions — click to filter by directory'}
1079
- >
1080
- {filterByCwd ? newCwd.split('/').pop() : 'All'}
1081
- </button>
1082
- )}
1083
- <ChevronDown className="w-3.5 h-3.5 text-zinc-500" />
1084
- </div>
1085
- )}
1086
-
1087
- {showSessionPicker && !selectedSession && (
1088
- <div className="absolute z-50 top-full left-0 right-0 mt-1 max-h-[300px] overflow-y-auto bg-zinc-800 border border-zinc-700 rounded-md shadow-xl">
1089
- {sessionsLoading ? (
1090
- <div className="flex items-center justify-center py-6">
1091
- <Loader2 className="w-4 h-4 animate-spin text-zinc-400" />
1092
- <span className="text-xs text-zinc-400 ml-2">Loading sessions...</span>
1093
- </div>
1094
- ) : sessions.length === 0 ? (
1095
- <div className="py-4 px-3 text-xs text-zinc-500 text-center">
1096
- {sessionSearch ? 'No sessions match your search' : 'No sessions found'}
1097
- </div>
1098
- ) : (
1099
- sessions.map((s) => (
1100
- <button
1101
- key={s.sessionId}
1102
- onClick={() => {
1103
- setSelectedSession(s);
1104
- setNewClaudeSession(s.sessionId);
1105
- if (!newCwd && s.projectPath) setNewCwd(s.projectPath);
1106
- if (!newTitle) setNewTitle(s.customName || '');
1107
- setShowSessionPicker(false);
1108
- }}
1109
- className="w-full text-left px-3 py-2.5 hover:bg-zinc-700 border-b border-zinc-700/50 last:border-0 transition-colors"
1110
- >
1111
- <div className="flex items-start gap-2">
1112
- <MessageSquare className="w-3.5 h-3.5 text-indigo-400 mt-0.5 flex-shrink-0" />
1113
- <div className="flex-1 min-w-0">
1114
- <div className="text-xs text-white truncate">
1115
- {s.customName || s.firstPrompt?.slice(0, 70) || 'Untitled session'}
1116
- </div>
1117
- <div className="flex items-center gap-2 mt-0.5">
1118
- <span className="text-[10px] text-indigo-400/70 flex items-center gap-0.5">
1119
- <FolderOpen className="w-2.5 h-2.5" />
1120
- {s.projectName}
1121
- </span>
1122
- <span className="text-[10px] text-zinc-500">{s.messageCount} msgs</span>
1123
- <span className="text-[10px] text-zinc-500 flex items-center gap-0.5">
1124
- <Clock className="w-2.5 h-2.5" />
1125
- {new Date(s.modified).toLocaleDateString()}
1126
- </span>
1127
- {s.starred && <span className="text-[10px]">★</span>}
1128
- </div>
1129
- {s.tags?.length > 0 && (
1130
- <div className="flex gap-1 mt-1">
1131
- {s.tags.map(t => (
1132
- <span key={t} className="text-[9px] px-1.5 py-0.5 bg-zinc-700 rounded text-zinc-400">{t}</span>
1133
- ))}
1134
- </div>
1135
- )}
1136
- </div>
1137
- </div>
1138
- </button>
1139
- ))
1140
- )}
1141
- </div>
1142
- )}
1143
- </div>
1144
- )}
1145
-
1146
- <div>
1147
- <label className="text-[11px] text-zinc-400 mb-1.5 block">Color</label>
1148
- <ColorPicker value={newColor} onChange={setNewColor} />
1149
- </div>
1150
-
1151
- <div className="flex gap-2 pt-1">
1152
- <button
1153
- onClick={addPane}
1154
- disabled={newAgentType === 'custom' && !newCustomCommand.trim()}
1155
- className="px-4 py-2 text-sm bg-indigo-600 text-white rounded-md hover:bg-indigo-500 disabled:opacity-50 disabled:cursor-not-allowed"
1156
- >
1157
- Create
1158
- </button>
1159
- <button
1160
- onClick={() => setShowAdd(false)}
1161
- className="px-4 py-2 text-sm border border-zinc-700 text-zinc-400 rounded-md hover:text-white hover:border-zinc-600"
1162
- >
1163
- Cancel
1164
- </button>
1165
- </div>
1166
- </div>
1167
- </div>
1168
- )}
1169
-
1170
- {/* Popped-out pane indicators */}
1171
- {poppedOut.size > 0 && (
1172
- <div className="flex items-center gap-2 px-4 py-1.5 border-b border-zinc-800 bg-zinc-900/50">
1173
- <span className="text-[10px] text-zinc-500">Popped out:</span>
1174
- {panes.filter(p => poppedOut.has(p.id)).map(p => (
1175
- <div key={p.id} className="flex items-center gap-0.5">
1176
- <button
1177
- onClick={() => openPopoutWindow(p)}
1178
- className="flex items-center gap-1 px-2 py-0.5 text-[10px] bg-zinc-800 border border-zinc-700 rounded-l text-zinc-400 hover:text-white hover:border-zinc-600"
1179
- title="Focus popout window"
1180
- >
1181
- <span className="w-1.5 h-1.5 rounded-full" style={{ backgroundColor: p.color }} />
1182
- {p.title}
1183
- </button>
1184
- <button
1185
- onClick={() => popIn(p.id)}
1186
- className="px-1 py-0.5 text-[10px] bg-zinc-800 border border-zinc-700 border-l-0 rounded-r text-zinc-500 hover:text-white hover:border-zinc-600"
1187
- title="Pop back into grid"
1188
- >
1189
- <ArrowLeftToLine className="w-2.5 h-2.5" />
1190
- </button>
1191
- </div>
1192
- ))}
1193
- </div>
1194
- )}
1195
-
1196
- {/* File Explorer + Terminal grid + Activity Panel */}
1197
- <div className="flex-1 flex overflow-hidden min-h-0">
1198
- {showFiles && (
1199
- <div className="w-64 flex-shrink-0">
1200
- <FileExplorer
1201
- onClose={() => setShowFiles(false)}
1202
- navigateTo={browseTarget}
1203
- />
1204
- </div>
1205
- )}
1206
- {visiblePanes.length === 0 && poppedOut.size === 0 ? (
1207
- <div className="flex-1 flex items-center justify-center">
1208
- <div className="text-center space-y-3">
1209
- {/* eslint-disable-next-line @next/next/no-img-element */}
1210
- <img src={`${basePath}/spaces_icon.png`} alt="Spaces" className="w-16 h-16 mx-auto opacity-30" />
1211
- <p className="text-zinc-500">No panes yet.</p>
1212
- <p className="text-zinc-600 text-xs">Add a pane to start a shell, Claude, Codex, Gemini, or any agent.</p>
1213
- <button
1214
- onClick={() => setShowAdd(true)}
1215
- className="px-4 py-2 text-sm bg-indigo-600 text-white rounded-md hover:bg-indigo-500 inline-flex items-center gap-2"
1216
- >
1217
- <Plus className="w-4 h-4" />
1218
- Add your first pane
1219
- </button>
1220
- </div>
1221
- </div>
1222
- ) : visiblePanes.length === 0 ? (
1223
- <div className="flex-1 flex items-center justify-center">
1224
- <div className="text-center space-y-2">
1225
- <p className="text-zinc-500 text-sm">All panes are popped out to separate windows.</p>
1226
- <button
1227
- onClick={() => setShowAdd(true)}
1228
- className="px-3 py-1.5 text-xs text-zinc-400 hover:text-white border border-zinc-700 rounded-md hover:border-zinc-600 inline-flex items-center gap-1.5"
1229
- >
1230
- <Plus className="w-3.5 h-3.5" />
1231
- Add another pane
1232
- </button>
1233
- </div>
1234
- </div>
1235
- ) : (
1236
- <div className="flex-1 flex flex-col overflow-hidden min-h-0">
1237
- <DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
1238
- <SortableContext items={visiblePanes.map(p => p.id)} strategy={verticalListSortingStrategy}>
1239
- <ResizablePaneGrid
1240
- panes={maximized ? visiblePanes.filter(p => p.id === maximized) : visiblePanes}
1241
- renderPane={(pane, dragHandleProps) => (
1242
- <TerminalPane
1243
- pane={pane}
1244
- onClose={closePane}
1245
- onUpdate={updatePane}
1246
- isMaximized={maximized === pane.id}
1247
- onToggleMaximize={toggleMaximize}
1248
- onMinimize={minimizePane}
1249
- onPopout={handlePopout}
1250
- onBrowse={handleBrowse}
1251
- terminalToken={terminalToken}
1252
- workspaceCollaboration={activeWorkspace?.collaboration}
1253
- dragHandleProps={dragHandleProps}
1254
- />
1255
- )}
1256
- />
1257
- </SortableContext>
1258
- </DndContext>
1259
-
1260
- {/* Minimized panes dock */}
1261
- {minimizedPanes.length > 0 && (
1262
- <div className="flex items-center gap-1.5 px-2 py-1.5 border-t border-zinc-800 flex-shrink-0 overflow-x-auto bg-zinc-950">
1263
- {minimizedPanes.map(pane => (
1264
- <button
1265
- key={pane.id}
1266
- onClick={() => restorePane(pane.id)}
1267
- className="flex items-center gap-1.5 px-2.5 py-1 text-[11px] rounded-md border border-zinc-700 hover:border-zinc-500 bg-zinc-800/50 hover:bg-zinc-700/50 text-zinc-400 hover:text-white transition-colors flex-shrink-0"
1268
- title={`Restore ${pane.title}`}
1269
- >
1270
- <span className="w-2 h-2 rounded-full flex-shrink-0" style={{ backgroundColor: pane.color }} />
1271
- <span className="truncate max-w-[100px]">{pane.title}</span>
1272
- <Maximize2 className="w-2.5 h-2.5 text-zinc-500" />
1273
- </button>
1274
- ))}
1275
- </div>
1276
- )}
1277
- </div>
1278
- )}
1279
-
1280
- {activeWorkspace?.collaboration && (
1281
- <ActivityPanel
1282
- workspaceId={activeWorkspace?.id ?? null}
1283
- panes={visiblePanes.map(p => ({ id: p.id, title: p.title, agentType: p.agentType, color: p.color }))}
1284
- collapsed={busPanelCollapsed}
1285
- onToggle={() => setBusPanelCollapsed(!busPanelCollapsed)}
1286
- />
1287
- )}
1288
- </div>
1289
- </div>
1290
- );
1291
- }
1
+ 'use client';
2
+
3
+ import React, { useState, useEffect, useCallback, useRef } from 'react';
4
+ import {
5
+ Plus, Loader2, Terminal, Search, MessageSquare, FolderOpen,
6
+ Clock, ChevronDown, ChevronRight, Save, FolderInput, Trash2, Pencil, Check, X,
7
+ Layers, Copy, Home, XCircle, ArrowLeftToLine, Globe, AlertCircle, Users, Brain, Maximize2, Wand2,
8
+ } from 'lucide-react';
9
+ import { useRouter } from 'next/navigation';
10
+ import { TerminalPane } from '@/components/terminal/terminal-pane';
11
+ import { DndContext, closestCenter, KeyboardSensor, PointerSensor, useSensor, useSensors, type DragEndEvent } from '@dnd-kit/core';
12
+ import { SortableContext, useSortable, verticalListSortingStrategy, arrayMove } from '@dnd-kit/sortable';
13
+ import { CSS } from '@dnd-kit/utilities';
14
+ import { Group as PanelGroup, Panel, Separator as PanelResizeHandle } from 'react-resizable-panels';
15
+ import { ColorPicker } from '@/components/common/color-picker';
16
+ import { DevDirectoryPicker } from '@/components/common/dev-directory-picker';
17
+ import { NodeSelector } from '@/components/network/node-selector';
18
+ import { TotpGate } from '@/components/auth/totp-gate';
19
+ import { AGENT_TYPES, AGENT_LIST } from '@/lib/agents';
20
+ import type { PaneData } from '@/lib/db/queries';
21
+ import type { SessionWithMeta, Workspace } from '@/types/claude';
22
+ import { useRemoteWorkspaces } from '@/hooks/use-sessions';
23
+ import { ActivityPanel } from '@/components/bus/activity-panel';
24
+ import { useSSEBusEvents } from '@/hooks/use-sse';
25
+ import { api } from '@/lib/api';
26
+ import { track } from '@/lib/telemetry';
27
+ import { LobeSettings } from '@/components/cortex/lobe-settings';
28
+ import { FileExplorer } from '@/components/files/file-explorer';
29
+ import { useTier } from '@/hooks/use-tier';
30
+ import { ProjectWizard } from '@/components/wizard/project-wizard';
31
+
32
+ function ResizablePaneGrid({ panes, renderPane }: {
33
+ panes: PaneData[];
34
+ renderPane: (pane: PaneData, dragHandleProps: Record<string, any>) => React.ReactNode;
35
+ }) {
36
+ if (panes.length === 0) return null;
37
+
38
+ // Single pane no resize handles needed
39
+ if (panes.length === 1) {
40
+ return (
41
+ <div className="flex-1 p-1 overflow-hidden">
42
+ <SortablePane id={panes[0].id}>
43
+ {(dragHandleProps) => renderPane(panes[0], dragHandleProps)}
44
+ </SortablePane>
45
+ </div>
46
+ );
47
+ }
48
+
49
+ // Split panes into rows: 2 per row for 2-4 panes, 3 per row for 5+
50
+ const cols = panes.length <= 4 ? 2 : 3;
51
+ const rows: PaneData[][] = [];
52
+ for (let i = 0; i < panes.length; i += cols) {
53
+ rows.push(panes.slice(i, i + cols));
54
+ }
55
+
56
+ return (
57
+ <PanelGroup orientation="vertical" className="flex-1 p-1">
58
+ {rows.map((row, rowIdx) => (
59
+ <React.Fragment key={`row-${rowIdx}`}>
60
+ {rowIdx > 0 && (
61
+ <PanelResizeHandle className="h-1.5 flex items-center justify-center group cursor-row-resize">
62
+ <div className="w-8 h-0.5 rounded-full bg-zinc-700 group-hover:bg-zinc-500 group-active:bg-indigo-500 transition-colors" />
63
+ </PanelResizeHandle>
64
+ )}
65
+ <Panel minSize={15}>
66
+ {row.length === 1 ? (
67
+ <div className="h-full p-0.5">
68
+ <SortablePane id={row[0].id}>
69
+ {(dragHandleProps) => renderPane(row[0], dragHandleProps)}
70
+ </SortablePane>
71
+ </div>
72
+ ) : (
73
+ <PanelGroup orientation="horizontal" className="h-full">
74
+ {row.map((pane, colIdx) => (
75
+ <React.Fragment key={pane.id}>
76
+ {colIdx > 0 && (
77
+ <PanelResizeHandle className="w-1.5 flex items-center justify-center group cursor-col-resize">
78
+ <div className="h-8 w-0.5 rounded-full bg-zinc-700 group-hover:bg-zinc-500 group-active:bg-indigo-500 transition-colors" />
79
+ </PanelResizeHandle>
80
+ )}
81
+ <Panel minSize={15}>
82
+ <div className="h-full p-0.5">
83
+ <SortablePane id={pane.id}>
84
+ {(dragHandleProps) => renderPane(pane, dragHandleProps)}
85
+ </SortablePane>
86
+ </div>
87
+ </Panel>
88
+ </React.Fragment>
89
+ ))}
90
+ </PanelGroup>
91
+ )}
92
+ </Panel>
93
+ </React.Fragment>
94
+ ))}
95
+ </PanelGroup>
96
+ );
97
+ }
98
+
99
+ function SortablePane({ id, children }: { id: string; children: (dragHandleProps: Record<string, any>) => React.ReactNode }) {
100
+ const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id });
101
+ const style = {
102
+ transform: CSS.Transform.toString(transform),
103
+ transition,
104
+ opacity: isDragging ? 0.5 : 1,
105
+ zIndex: isDragging ? 50 : 'auto' as any,
106
+ };
107
+ return (
108
+ <div ref={setNodeRef} style={style} className="h-full">
109
+ {children({ ...attributes, ...listeners })}
110
+ </div>
111
+ );
112
+ }
113
+
114
+ export default function TerminalPage() {
115
+ return (
116
+ <TotpGate>
117
+ {(terminalToken) => <TerminalPageInner terminalToken={terminalToken} />}
118
+ </TotpGate>
119
+ );
120
+ }
121
+
122
+ function TerminalPageInner({ terminalToken }: { terminalToken: string }) {
123
+ const router = useRouter();
124
+ const { basePath, hasNetwork, hasCollaboration, hasCortex } = useTier();
125
+ const [panes, setPanes] = useState<PaneData[]>([]);
126
+ const [loading, setLoading] = useState(true);
127
+ const [wsLoading, setWsLoading] = useState(true);
128
+ const [showAdd, setShowAdd] = useState(false);
129
+ const [maximized, setMaximized] = useState<string | null>(null);
130
+ const [minimized, setMinimized] = useState<Set<string>>(new Set());
131
+ const [poppedOut, setPoppedOut] = useState<Set<string>>(new Set());
132
+ const [entered, setEntered] = useState(false);
133
+ const [busPanelCollapsed, setBusPanelCollapsed] = useState(true);
134
+
135
+ // SSE bus events for real-time updates
136
+ useSSEBusEvents();
137
+
138
+ // Workspace state
139
+ const [workspaces, setWorkspaces] = useState<Workspace[]>([]);
140
+ const [activeWorkspace, setActiveWorkspace] = useState<Workspace | null>(null);
141
+ const [showWsPicker, setShowWsPicker] = useState(false);
142
+ const [showSaveAs, setShowSaveAs] = useState(false);
143
+ const [showWizard, setShowWizard] = useState(false);
144
+ const [saveAsName, setSaveAsName] = useState('');
145
+ const [saveAsColor, setSaveAsColor] = useState('#6366f1');
146
+ const [editingWs, setEditingWs] = useState<number | null>(null);
147
+ const [editWsName, setEditWsName] = useState('');
148
+ const [lobeOpenWs, setLobeOpenWs] = useState<number | null>(null);
149
+ const [showFiles, setShowFiles] = useState(false);
150
+ const [browseTarget, setBrowseTarget] = useState<string | null>(null);
151
+
152
+ const handleBrowse = useCallback((cwd: string) => {
153
+ setBrowseTarget(cwd);
154
+ setShowFiles(true);
155
+ }, []);
156
+
157
+ // New pane form state
158
+ const [newTitle, setNewTitle] = useState('');
159
+ const [newCwd, setNewCwd] = useState('');
160
+ const [newColor, setNewColor] = useState('#6366f1');
161
+ const [newClaudeSession, setNewClaudeSession] = useState('');
162
+ const [newAgentType, setNewAgentType] = useState('claude');
163
+ const [newAgentMode, setNewAgentMode] = useState<'new' | 'resume'>('new');
164
+ const [newCustomCommand, setNewCustomCommand] = useState('');
165
+ const [newNodeId, setNewNodeId] = useState('');
166
+
167
+ // Remote workspaces
168
+ const remoteWorkspacesQuery = useRemoteWorkspaces();
169
+ const [collapsedNodes, setCollapsedNodes] = useState<Set<string>>(new Set());
170
+ const toggleNodeCollapse = useCallback((nodeId: string) => {
171
+ setCollapsedNodes(prev => {
172
+ const next = new Set(prev);
173
+ if (next.has(nodeId)) next.delete(nodeId); else next.add(nodeId);
174
+ return next;
175
+ });
176
+ }, []);
177
+
178
+ // Session picker state
179
+ const [sessionSearch, setSessionSearch] = useState('');
180
+ const [sessions, setSessions] = useState<SessionWithMeta[]>([]);
181
+ const [sessionsLoading, setSessionsLoading] = useState(false);
182
+ const [showSessionPicker, setShowSessionPicker] = useState(false);
183
+ const [selectedSession, setSelectedSession] = useState<SessionWithMeta | null>(null);
184
+ const [filterByCwd, setFilterByCwd] = useState(true);
185
+ const pickerRef = useRef<HTMLDivElement>(null);
186
+ const wsPickerRef = useRef<HTMLDivElement>(null);
187
+ const channelRef = useRef<BroadcastChannel | null>(null);
188
+
189
+ // ─── Data Loading ──────────────────────────────────────────
190
+
191
+ const loadWorkspaces = useCallback(async () => {
192
+ const res = await fetch(api('/api/workspaces'));
193
+ const data = await res.json();
194
+ setWorkspaces(data);
195
+ const active = data.find((w: Workspace) => w.isActive);
196
+ setActiveWorkspace(active || null);
197
+ setWsLoading(false);
198
+ return active || null;
199
+ }, []);
200
+
201
+ const loadPanes = useCallback(async () => {
202
+ const res = await fetch(api('/api/panes'));
203
+ const data = await res.json();
204
+ setPanes(data);
205
+ // Track which panes were popped out
206
+ const popped = new Set<string>();
207
+ for (const p of data) {
208
+ if (p.isPopout) popped.add(p.id);
209
+ }
210
+ setPoppedOut(popped);
211
+ setLoading(false);
212
+ }, []);
213
+
214
+ useEffect(() => {
215
+ loadWorkspaces();
216
+ }, [loadWorkspaces]);
217
+
218
+ // Only load panes after entering a workspace
219
+ useEffect(() => {
220
+ if (entered) {
221
+ loadPanes();
222
+ }
223
+ }, [entered, loadPanes]);
224
+
225
+ // ─── BroadcastChannel for cross-window sync ────────────────
226
+
227
+ useEffect(() => {
228
+ const channel = new BroadcastChannel('spaces-panes');
229
+ channelRef.current = channel;
230
+
231
+ channel.onmessage = (event) => {
232
+ const msg = event.data;
233
+ if (msg.type === 'popout-opened') {
234
+ setPoppedOut(prev => new Set(prev).add(msg.paneId));
235
+ } else if (msg.type === 'popout-closed') {
236
+ setPoppedOut(prev => {
237
+ const next = new Set(prev);
238
+ next.delete(msg.paneId);
239
+ return next;
240
+ });
241
+ // Optimistically mark pane as not popped out in local state.
242
+ // Don't call loadPanes() here the sendBeacon that sets isPopout=false
243
+ // in the DB is still in-flight, and loadPanes() would re-read the stale
244
+ // isPopout=true value and put the pane right back into poppedOut.
245
+ setPanes(prev => prev.map(p =>
246
+ p.id === msg.paneId ? { ...p, isPopout: false } : p
247
+ ));
248
+ } else if (msg.type === 'pane-updated') {
249
+ setPanes(prev => prev.map(p =>
250
+ p.id === msg.paneId ? { ...p, ...msg.data } : p
251
+ ));
252
+ }
253
+ };
254
+
255
+ return () => channel.close();
256
+ }, [loadPanes]);
257
+
258
+ // Counter that increments on workspace switch to trigger popout restore
259
+ const [restoreGen, setRestoreGen] = useState(0);
260
+
261
+ // Auto-restore popped-out panes on load and after workspace switch
262
+ useEffect(() => {
263
+ if (loading) return;
264
+ for (const pane of panes) {
265
+ if (pane.isPopout) {
266
+ openPopoutWindow(pane);
267
+ }
268
+ }
269
+ // eslint-disable-next-line react-hooks/exhaustive-deps
270
+ }, [loading, restoreGen]);
271
+
272
+ // ─── Session Search ────────────────────────────────────────
273
+
274
+ useEffect(() => {
275
+ if (!(AGENT_TYPES[newAgentType]?.supportsResume && newAgentMode === 'resume')) return;
276
+ setSessionsLoading(true);
277
+ const timer = setTimeout(() => {
278
+ const sp = new URLSearchParams({
279
+ sortBy: 'modified', sortDir: 'DESC', limit: '200',
280
+ });
281
+ if (sessionSearch) sp.set('search', sessionSearch);
282
+ if (filterByCwd && newCwd) sp.set('projectPath', newCwd);
283
+ sp.set('agentType', newAgentType);
284
+ fetch(api(`/api/sessions?${sp}`))
285
+ .then(r => r.json())
286
+ .then(data => { setSessions(data.sessions || []); setSessionsLoading(false); })
287
+ .catch(() => setSessionsLoading(false));
288
+ }, sessionSearch ? 300 : 0);
289
+ return () => clearTimeout(timer);
290
+ }, [newAgentType, newAgentMode, sessionSearch, filterByCwd, newCwd]);
291
+
292
+ // Close pickers on outside click
293
+ useEffect(() => {
294
+ function handleClick(e: MouseEvent) {
295
+ if (pickerRef.current && !pickerRef.current.contains(e.target as Node)) {
296
+ setShowSessionPicker(false);
297
+ }
298
+ if (wsPickerRef.current && !wsPickerRef.current.contains(e.target as Node)) {
299
+ setShowWsPicker(false);
300
+ }
301
+ }
302
+ document.addEventListener('mousedown', handleClick);
303
+ return () => document.removeEventListener('mousedown', handleClick);
304
+ }, []);
305
+
306
+ // ─── Pane Operations ──────────────────────────────────────
307
+
308
+ const addPane = useCallback(async () => {
309
+ const agent = AGENT_TYPES[newAgentType];
310
+ const cwd = newCwd
311
+ || (selectedSession?.projectPath)
312
+ || '/';
313
+
314
+ // For agent resume, pass the session ID; for new agent sessions, pass 'new'
315
+ let claudeSessionId: string | undefined;
316
+ if (newAgentType !== 'shell') {
317
+ if (newAgentMode === 'resume' && agent?.supportsResume) {
318
+ claudeSessionId = selectedSession?.sessionId || newClaudeSession;
319
+ } else {
320
+ claudeSessionId = 'new';
321
+ }
322
+ }
323
+
324
+ const title = newTitle
325
+ || (selectedSession ? (selectedSession.customName || selectedSession.firstPrompt?.slice(0, 50) || agent?.name || 'Agent') : '')
326
+ || (newAgentType === 'shell' ? 'Terminal' : agent?.name || 'Agent');
327
+
328
+ // Auto-opt-in to collaboration for agent panes when workspace has collaboration enabled
329
+ const autoCollab = activeWorkspace?.collaboration && newAgentType !== 'shell';
330
+
331
+ const res = await fetch(api('/api/panes'), {
332
+ method: 'POST',
333
+ headers: { 'Content-Type': 'application/json' },
334
+ body: JSON.stringify({
335
+ title, color: newColor, cwd, claudeSessionId,
336
+ agentType: newAgentType,
337
+ customCommand: newAgentType === 'custom' ? newCustomCommand : undefined,
338
+ nodeId: newNodeId || undefined,
339
+ isCollaborating: autoCollab || undefined,
340
+ }),
341
+ });
342
+ const pane = await res.json();
343
+ setPanes(prev => [...prev, pane]);
344
+ track('pane_created', { agentType: newAgentType });
345
+ setShowAdd(false);
346
+ setNewTitle('');
347
+ setNewCwd('');
348
+ setNewColor('#6366f1');
349
+ setNewClaudeSession('');
350
+ setNewAgentType('shell');
351
+ setNewAgentMode('new');
352
+ setNewCustomCommand('');
353
+ setNewNodeId('');
354
+ setSelectedSession(null);
355
+ setSessionSearch('');
356
+ }, [newTitle, newCwd, newColor, newClaudeSession, newAgentType, newAgentMode, newCustomCommand, newNodeId, selectedSession]);
357
+
358
+ const closePane = useCallback(async (id: string) => {
359
+ const closing = panes.find(p => p.id === id);
360
+ await fetch(api(`/api/panes/${id}`), { method: 'DELETE' });
361
+ setPanes(prev => prev.filter(p => p.id !== id));
362
+ if (closing) track('pane_closed', { agentType: closing.agentType });
363
+ if (maximized === id) setMaximized(null);
364
+ setPoppedOut(prev => {
365
+ const next = new Set(prev);
366
+ next.delete(id);
367
+ return next;
368
+ });
369
+ }, [maximized, panes]);
370
+
371
+ const updatePane = useCallback(async (id: string, data: Partial<PaneData>) => {
372
+ await fetch(api(`/api/panes/${id}`), {
373
+ method: 'PUT',
374
+ headers: { 'Content-Type': 'application/json' },
375
+ body: JSON.stringify(data),
376
+ });
377
+ setPanes(prev => prev.map(p => p.id === id ? { ...p, ...data } : p));
378
+ }, []);
379
+
380
+ const toggleMaximize = useCallback((id: string) => {
381
+ setMaximized(prev => prev === id ? null : id);
382
+ }, []);
383
+
384
+ const minimizePane = useCallback((id: string) => {
385
+ setMinimized(prev => new Set(prev).add(id));
386
+ if (maximized === id) setMaximized(null);
387
+ }, [maximized]);
388
+
389
+ const restorePane = useCallback((id: string) => {
390
+ setMinimized(prev => { const next = new Set(prev); next.delete(id); return next; });
391
+ }, []);
392
+
393
+ // ─── Drag to reorder ──────────────────────────────────────
394
+ const sensors = useSensors(
395
+ useSensor(PointerSensor, { activationConstraint: { distance: 8 } }),
396
+ useSensor(KeyboardSensor),
397
+ );
398
+
399
+ const handleDragEnd = useCallback((event: DragEndEvent) => {
400
+ const { active, over } = event;
401
+ if (!over || active.id === over.id) return;
402
+ setPanes(prev => {
403
+ const oldIndex = prev.findIndex(p => p.id === active.id);
404
+ const newIndex = prev.findIndex(p => p.id === over.id);
405
+ if (oldIndex === -1 || newIndex === -1) return prev;
406
+ const reordered = arrayMove(prev, oldIndex, newIndex);
407
+ // Persist sort order
408
+ reordered.forEach((p, i) => {
409
+ if (p.sortOrder !== i) {
410
+ fetch(api(`/api/panes/${p.id}`), {
411
+ method: 'PUT',
412
+ headers: { 'Content-Type': 'application/json' },
413
+ body: JSON.stringify({ sortOrder: i }),
414
+ }).catch(() => {});
415
+ }
416
+ });
417
+ return reordered.map((p, i) => ({ ...p, sortOrder: i }));
418
+ });
419
+ }, []);
420
+
421
+ // ─── Popout ────────────────────────────────────────────────
422
+
423
+ const openPopoutWindow = useCallback((pane: PaneData) => {
424
+ const w = pane.winWidth || 900;
425
+ const h = pane.winHeight || 600;
426
+ const x = pane.winX ?? Math.round(screen.width / 2 - w / 2);
427
+ const y = pane.winY ?? Math.round(screen.height / 2 - h / 2);
428
+ const features = `left=${x},top=${y},width=${w},height=${h},menubar=no,toolbar=no,location=no,status=no`;
429
+
430
+ window.open(api(`/terminal/pane/${pane.id}`), `spaces-pane-${pane.id}`, features);
431
+ setPoppedOut(prev => new Set(prev).add(pane.id));
432
+
433
+ // Mark in DB
434
+ fetch(api(`/api/panes/${pane.id}`), {
435
+ method: 'PUT',
436
+ headers: { 'Content-Type': 'application/json' },
437
+ body: JSON.stringify({ isPopout: true, winX: x, winY: y, winWidth: w, winHeight: h }),
438
+ });
439
+ }, []);
440
+
441
+ const handlePopout = useCallback((id: string) => {
442
+ const pane = panes.find(p => p.id === id);
443
+ if (pane) openPopoutWindow(pane);
444
+ }, [panes, openPopoutWindow]);
445
+
446
+ // ─── Pop in (return popout to grid) ──────────────────────
447
+
448
+ const popIn = useCallback(async (id: string) => {
449
+ // Tell the popout window to close itself
450
+ channelRef.current?.postMessage({ type: 'close-popouts' });
451
+ // Mark as not popped out in DB
452
+ await fetch(api(`/api/panes/${id}`), {
453
+ method: 'PUT',
454
+ headers: { 'Content-Type': 'application/json' },
455
+ body: JSON.stringify({ isPopout: false }),
456
+ });
457
+ setPoppedOut(prev => {
458
+ const next = new Set(prev);
459
+ next.delete(id);
460
+ return next;
461
+ });
462
+ }, []);
463
+
464
+ // ─── Close all popout windows ─────────────────────────────
465
+
466
+ const closeAllPopouts = useCallback(() => {
467
+ if (poppedOut.size > 0) {
468
+ channelRef.current?.postMessage({ type: 'close-popouts' });
469
+ setPoppedOut(new Set());
470
+ }
471
+ }, [poppedOut]);
472
+
473
+ // ─── Workspace Operations ──────────────────────────────────
474
+
475
+ const switchWorkspace = useCallback(async (wsId: number) => {
476
+ // Close all popout windows first (saves their positions)
477
+ closeAllPopouts();
478
+ await fetch(api('/api/workspaces'), {
479
+ method: 'POST',
480
+ headers: { 'Content-Type': 'application/json' },
481
+ body: JSON.stringify({ action: 'switch', workspaceId: wsId }),
482
+ });
483
+ setMaximized(null);
484
+ await loadWorkspaces();
485
+ await loadPanes();
486
+ setShowWsPicker(false);
487
+ if (entered) {
488
+ track('workspace_switched');
489
+ } else {
490
+ track('workspace_entered');
491
+ }
492
+ setEntered(true);
493
+ // Trigger popout restore for the new workspace's panes
494
+ setRestoreGen(prev => prev + 1);
495
+ }, [loadWorkspaces, loadPanes, closeAllPopouts, entered]);
496
+
497
+ const saveWorkspaceAs = useCallback(async () => {
498
+ if (!saveAsName.trim() || !activeWorkspace) return;
499
+ await fetch(api('/api/workspaces'), {
500
+ method: 'POST',
501
+ headers: { 'Content-Type': 'application/json' },
502
+ body: JSON.stringify({ action: 'duplicate', sourceId: activeWorkspace.id, name: saveAsName.trim(), color: saveAsColor }),
503
+ });
504
+ setSaveAsName('');
505
+ setSaveAsColor('#6366f1');
506
+ setShowSaveAs(false);
507
+ await loadWorkspaces();
508
+ }, [saveAsName, saveAsColor, activeWorkspace, loadWorkspaces]);
509
+
510
+ const createNewWorkspace = useCallback(async () => {
511
+ const res = await fetch(api('/api/workspaces'), {
512
+ method: 'POST',
513
+ headers: { 'Content-Type': 'application/json' },
514
+ body: JSON.stringify({ name: 'New Space', color: '#6366f1' }),
515
+ });
516
+ const ws = await res.json();
517
+ track('workspace_created');
518
+ await switchWorkspace(ws.id);
519
+ }, [switchWorkspace]);
520
+
521
+ const deleteWorkspace = useCallback(async (wsId: number) => {
522
+ closeAllPopouts();
523
+ await fetch(api(`/api/workspaces/${wsId}`), { method: 'DELETE' });
524
+ track('workspace_deleted');
525
+ await loadWorkspaces();
526
+ await loadPanes();
527
+ setShowWsPicker(false);
528
+ }, [loadWorkspaces, loadPanes, closeAllPopouts]);
529
+
530
+ const closeWorkspace = useCallback(() => {
531
+ closeAllPopouts();
532
+ setPanes([]);
533
+ setEntered(false);
534
+ }, [closeAllPopouts]);
535
+
536
+ const renameWorkspace = useCallback(async (wsId: number, name: string) => {
537
+ await fetch(api(`/api/workspaces/${wsId}`), {
538
+ method: 'PUT',
539
+ headers: { 'Content-Type': 'application/json' },
540
+ body: JSON.stringify({ name }),
541
+ });
542
+ setEditingWs(null);
543
+ await loadWorkspaces();
544
+ }, [loadWorkspaces]);
545
+
546
+ // ─── Render ────────────────────────────────────────────────
547
+
548
+ const visiblePanes = panes.filter(p => !poppedOut.has(p.id) && !minimized.has(p.id));
549
+ const minimizedPanes = panes.filter(p => minimized.has(p.id));
550
+
551
+ // ─── Workspace chooser (before entering) ─────────────────
552
+
553
+ if (!entered) {
554
+ return (
555
+ <div className="h-screen flex flex-col bg-zinc-950 text-zinc-100">
556
+ <div className="absolute top-4 left-4">
557
+ <button
558
+ onClick={() => router.push('/')}
559
+ className="flex items-center gap-2 px-3 py-1.5 text-xs text-zinc-500 hover:text-white hover:bg-zinc-800 rounded-md transition-colors"
560
+ >
561
+ <Home className="w-3.5 h-3.5" />
562
+ Home
563
+ </button>
564
+ </div>
565
+ <div className="flex-1 flex items-center justify-center p-8 overflow-y-auto min-h-0">
566
+ <div className="w-full max-w-2xl my-auto">
567
+ <div className="text-center mb-10">
568
+ {/* eslint-disable-next-line @next/next/no-img-element */}
569
+ <img src={`${basePath}/spaces_icon.png`} alt="Spaces" className="w-16 h-16 mx-auto mb-4" />
570
+ <h1 className="text-2xl font-bold mb-2">Spaces</h1>
571
+ <p className="text-zinc-400 text-sm">Choose a workspace to open, or create a new one.</p>
572
+ </div>
573
+
574
+ {wsLoading ? (
575
+ <div className="flex justify-center py-12">
576
+ <Loader2 className="w-6 h-6 animate-spin text-indigo-500" />
577
+ </div>
578
+ ) : (
579
+ <>
580
+ <div className="grid grid-cols-2 gap-3 mb-6">
581
+ {workspaces.map((ws) => (
582
+ <div
583
+ key={ws.id}
584
+ className={`relative bg-zinc-900 border rounded-lg transition-all ${
585
+ lobeOpenWs === ws.id ? 'border-zinc-600' : 'border-zinc-800 hover:border-zinc-600 hover:bg-zinc-800/50'
586
+ }`}
587
+ >
588
+ <button
589
+ onClick={() => switchWorkspace(ws.id)}
590
+ className="flex items-center gap-3 p-4 text-left w-full group"
591
+ >
592
+ <span
593
+ className="w-3.5 h-3.5 rounded-full flex-shrink-0"
594
+ style={{ backgroundColor: ws.color }}
595
+ />
596
+ <div className="flex-1 min-w-0">
597
+ <div className="text-sm font-medium text-white truncate">{ws.name}</div>
598
+ <div className="text-xs text-zinc-500 mt-0.5">
599
+ {ws.paneCount || 0} pane{(ws.paneCount || 0) !== 1 ? 's' : ''}
600
+ {ws.isActive && <span className="text-indigo-400 ml-2">last active</span>}
601
+ </div>
602
+ </div>
603
+ <Terminal className="w-4 h-4 text-zinc-600 group-hover:text-zinc-400 transition-colors" />
604
+ </button>
605
+ {hasCortex && (
606
+ <button
607
+ onClick={() => setLobeOpenWs(lobeOpenWs === ws.id ? null : ws.id)}
608
+ className={`absolute top-2 right-2 p-1 rounded transition-colors ${
609
+ lobeOpenWs === ws.id ? 'text-purple-400 bg-purple-500/10' : 'text-zinc-700 hover:text-zinc-400'
610
+ }`}
611
+ title="Knowledge lobe settings"
612
+ >
613
+ <Brain className="w-3.5 h-3.5" />
614
+ </button>
615
+ )}
616
+ {lobeOpenWs === ws.id && (
617
+ <div className="px-4 pb-4 border-t border-zinc-800/50 pt-3">
618
+ <LobeSettings workspaceId={ws.id} workspaceName={ws.name} />
619
+ </div>
620
+ )}
621
+ </div>
622
+ ))}
623
+ </div>
624
+
625
+ <div className="flex justify-center gap-3">
626
+ <button
627
+ onClick={createNewWorkspace}
628
+ className="flex items-center gap-2 px-4 py-2.5 text-sm border border-zinc-700 text-zinc-400 rounded-lg hover:text-white hover:border-zinc-500 transition-colors"
629
+ >
630
+ <Plus className="w-4 h-4" />
631
+ New Workspace
632
+ </button>
633
+ <button
634
+ onClick={() => setShowWizard(true)}
635
+ className="flex items-center gap-2 px-4 py-2.5 text-sm border border-dashed border-indigo-500/50 text-indigo-400 rounded-lg hover:text-indigo-300 hover:border-indigo-400 hover:bg-indigo-500/10 transition-colors"
636
+ >
637
+ <Wand2 className="w-4 h-4" />
638
+ Plan a Project
639
+ </button>
640
+ </div>
641
+
642
+ {/* Remote workspaces from network nodes */}
643
+ {hasNetwork && remoteWorkspacesQuery.isLoading && (
644
+ <div className="flex items-center justify-center gap-2 py-4 mt-6 border-t border-zinc-800">
645
+ <Loader2 className="w-4 h-4 animate-spin text-zinc-500" />
646
+ <span className="text-xs text-zinc-500">Loading network spaces...</span>
647
+ </div>
648
+ )}
649
+
650
+ {hasNetwork && remoteWorkspacesQuery.data?.remote && remoteWorkspacesQuery.data.remote.length > 0 && (
651
+ <div className="mt-6 border-t border-zinc-800 pt-6">
652
+ <div className="flex items-center gap-2 mb-4">
653
+ <Globe className="w-4 h-4 text-zinc-500" />
654
+ <h2 className="text-sm font-medium text-zinc-400">Network Spaces</h2>
655
+ </div>
656
+
657
+ {remoteWorkspacesQuery.data.remote.map((node) => (
658
+ <div key={node.nodeId} className="mb-3">
659
+ <button
660
+ onClick={() => toggleNodeCollapse(node.nodeId)}
661
+ className="flex items-center gap-2 w-full px-2 py-1.5 text-left text-xs text-zinc-400 hover:text-white rounded-md hover:bg-zinc-800/50 transition-colors"
662
+ >
663
+ {collapsedNodes.has(node.nodeId) ? (
664
+ <ChevronRight className="w-3.5 h-3.5 text-zinc-600" />
665
+ ) : (
666
+ <ChevronDown className="w-3.5 h-3.5 text-zinc-600" />
667
+ )}
668
+ <Globe className="w-3 h-3 text-zinc-600" />
669
+ <span className="font-medium">{node.nodeName}</span>
670
+ <span className="text-zinc-600">
671
+ {node.workspaces.length} space{node.workspaces.length !== 1 ? 's' : ''}
672
+ </span>
673
+ </button>
674
+
675
+ {!collapsedNodes.has(node.nodeId) && (
676
+ <div className="grid grid-cols-2 gap-2 mt-2 ml-5">
677
+ {node.workspaces.map((ws) => (
678
+ <button
679
+ key={`${node.nodeId}-${ws.id}`}
680
+ onClick={() => {
681
+ router.push(`/terminal/remote/${node.nodeId}/${ws.id}`);
682
+ }}
683
+ className="flex items-center gap-3 p-3 bg-zinc-900/50 border border-zinc-800/50 rounded-lg hover:border-zinc-600 hover:bg-zinc-800/30 transition-colors text-left group"
684
+ >
685
+ <span
686
+ className="w-3 h-3 rounded-full flex-shrink-0 ring-1 ring-zinc-700"
687
+ style={{ backgroundColor: ws.color }}
688
+ />
689
+ <div className="flex-1 min-w-0">
690
+ <div className="text-xs font-medium text-zinc-300 truncate">{ws.name}</div>
691
+ <div className="text-[10px] text-zinc-600 mt-0.5">
692
+ {ws.paneCount || 0} pane{(ws.paneCount || 0) !== 1 ? 's' : ''}
693
+ </div>
694
+ </div>
695
+ <Globe className="w-3 h-3 text-zinc-700 group-hover:text-zinc-500 transition-colors" />
696
+ </button>
697
+ ))}
698
+ </div>
699
+ )}
700
+ </div>
701
+ ))}
702
+
703
+ {/* Error indicators for nodes that failed */}
704
+ {remoteWorkspacesQuery.data.errors && remoteWorkspacesQuery.data.errors.length > 0 && (
705
+ <div className="mt-3 space-y-1">
706
+ {remoteWorkspacesQuery.data.errors.map((err) => (
707
+ <div key={err.nodeId} className="flex items-center gap-2 px-2 py-1.5 text-[10px] text-zinc-600">
708
+ <AlertCircle className="w-3 h-3 text-red-500/50" />
709
+ <span>{err.nodeName}: {err.error}</span>
710
+ </div>
711
+ ))}
712
+ </div>
713
+ )}
714
+ </div>
715
+ )}
716
+ </>
717
+ )}
718
+ </div>
719
+ </div>
720
+
721
+ <ProjectWizard
722
+ isOpen={showWizard}
723
+ onClose={() => setShowWizard(false)}
724
+ onLaunch={(wsId) => {
725
+ setShowWizard(false);
726
+ switchWorkspace(wsId);
727
+ }}
728
+ />
729
+ </div>
730
+ );
731
+ }
732
+
733
+ if (loading) {
734
+ return (
735
+ <div className="flex items-center justify-center h-screen bg-zinc-950">
736
+ <Loader2 className="w-6 h-6 animate-spin text-indigo-500" />
737
+ </div>
738
+ );
739
+ }
740
+
741
+ return (
742
+ <div className="h-screen flex flex-col bg-zinc-950 text-zinc-100">
743
+ {/* Header */}
744
+ <div className="flex items-center justify-between px-4 py-2 border-b border-zinc-800 flex-shrink-0">
745
+ <div className="flex items-center gap-3">
746
+ <button
747
+ onClick={closeWorkspace}
748
+ className="p-1.5 -ml-1 text-zinc-500 hover:text-white hover:bg-zinc-800 rounded-md transition-colors"
749
+ title="Close space and go home"
750
+ >
751
+ <Home className="w-4 h-4" />
752
+ </button>
753
+ <div className="w-px h-4 bg-zinc-800" />
754
+ <h1 className="text-sm font-semibold">Spaces</h1>
755
+
756
+ {/* Workspace selector */}
757
+ <div ref={wsPickerRef} className="relative">
758
+ <button
759
+ onClick={() => setShowWsPicker(!showWsPicker)}
760
+ className="flex items-center gap-1.5 px-2.5 py-1 text-xs bg-zinc-800 border border-zinc-700 rounded-md hover:border-zinc-600 transition-colors"
761
+ >
762
+ {activeWorkspace && (
763
+ <span className="w-2 h-2 rounded-full flex-shrink-0" style={{ backgroundColor: activeWorkspace.color }} />
764
+ )}
765
+ <span className="max-w-[150px] truncate">{activeWorkspace?.name || 'No space'}</span>
766
+ <ChevronDown className="w-3 h-3 text-zinc-500" />
767
+ </button>
768
+
769
+ {showWsPicker && (
770
+ <div className="absolute z-50 top-full left-0 mt-1 w-72 bg-zinc-800 border border-zinc-700 rounded-md shadow-xl overflow-hidden">
771
+ <div className="p-2 border-b border-zinc-700/50 text-[10px] text-zinc-500 uppercase tracking-wider font-medium">
772
+ Switch Space
773
+ </div>
774
+ <div className="max-h-[300px] overflow-y-auto">
775
+ {workspaces.map((ws) => (
776
+ <div key={ws.id} className="flex items-center gap-2 hover:bg-zinc-700/50 transition-colors group">
777
+ {editingWs === ws.id ? (
778
+ <div className="flex items-center gap-1 flex-1 px-3 py-2">
779
+ <input
780
+ autoFocus
781
+ value={editWsName}
782
+ onChange={(e) => setEditWsName(e.target.value)}
783
+ onKeyDown={(e) => {
784
+ if (e.key === 'Enter') renameWorkspace(ws.id, editWsName);
785
+ if (e.key === 'Escape') setEditingWs(null);
786
+ }}
787
+ className="flex-1 bg-transparent border border-zinc-600 rounded px-1.5 py-0.5 text-xs focus:outline-none focus:border-indigo-400"
788
+ />
789
+ <button onClick={() => renameWorkspace(ws.id, editWsName)} className="text-green-400 hover:text-green-300">
790
+ <Check className="w-3 h-3" />
791
+ </button>
792
+ </div>
793
+ ) : (
794
+ <>
795
+ <button
796
+ onClick={() => switchWorkspace(ws.id)}
797
+ className="flex items-center gap-2 flex-1 px-3 py-2 text-left"
798
+ >
799
+ <span className="w-2.5 h-2.5 rounded-full flex-shrink-0" style={{ backgroundColor: ws.color }} />
800
+ <span className="text-xs text-white truncate flex-1">{ws.name}</span>
801
+ <span className="text-[10px] text-zinc-500">{ws.paneCount || 0} panes</span>
802
+ {ws.isActive && <span className="text-[10px] text-indigo-400">active</span>}
803
+ </button>
804
+ <div className="flex items-center gap-0.5 pr-2 opacity-0 group-hover:opacity-100">
805
+ <button
806
+ onClick={(e) => { e.stopPropagation(); setEditingWs(ws.id); setEditWsName(ws.name); }}
807
+ className="p-1 text-zinc-500 hover:text-white rounded"
808
+ title="Rename"
809
+ >
810
+ <Pencil className="w-2.5 h-2.5" />
811
+ </button>
812
+ {!ws.isActive && workspaces.length > 1 && (
813
+ <button
814
+ onClick={(e) => {
815
+ e.stopPropagation();
816
+ if (confirm(`Delete space "${ws.name}" and all its panes?`)) {
817
+ deleteWorkspace(ws.id);
818
+ }
819
+ }}
820
+ className="p-1 text-zinc-500 hover:text-red-400 rounded"
821
+ title="Delete"
822
+ >
823
+ <Trash2 className="w-2.5 h-2.5" />
824
+ </button>
825
+ )}
826
+ </div>
827
+ </>
828
+ )}
829
+ </div>
830
+ ))}
831
+ </div>
832
+
833
+ <div className="border-t border-zinc-700/50 p-1.5 flex gap-1">
834
+ <button
835
+ onClick={createNewWorkspace}
836
+ className="flex items-center gap-1.5 px-2.5 py-1.5 text-[11px] text-zinc-400 hover:text-white hover:bg-zinc-700 rounded transition-colors flex-1"
837
+ >
838
+ <Plus className="w-3 h-3" /> New Empty
839
+ </button>
840
+ <button
841
+ onClick={() => { setShowWizard(true); setShowWsPicker(false); }}
842
+ className="flex items-center gap-1.5 px-2.5 py-1.5 text-[11px] text-indigo-400 hover:text-indigo-300 hover:bg-indigo-500/10 rounded transition-colors flex-1"
843
+ >
844
+ <Wand2 className="w-3 h-3" /> Plan Project
845
+ </button>
846
+ <button
847
+ onClick={() => { setShowSaveAs(true); setShowWsPicker(false); }}
848
+ className="flex items-center gap-1.5 px-2.5 py-1.5 text-[11px] text-zinc-400 hover:text-white hover:bg-zinc-700 rounded transition-colors flex-1"
849
+ >
850
+ <Copy className="w-3 h-3" /> Duplicate
851
+ </button>
852
+ </div>
853
+ </div>
854
+ )}
855
+ </div>
856
+
857
+ <span className="text-[11px] text-zinc-500">
858
+ {visiblePanes.length} pane{visiblePanes.length !== 1 ? 's' : ''}
859
+ {poppedOut.size > 0 && ` + ${poppedOut.size} popped out`}
860
+ </span>
861
+ </div>
862
+
863
+ <div className="flex items-center gap-2">
864
+ <button
865
+ onClick={() => setShowFiles(!showFiles)}
866
+ className={`flex items-center gap-1.5 px-2.5 py-1.5 text-xs rounded-md border transition-colors ${
867
+ showFiles
868
+ ? 'border-amber-500/50 bg-amber-600/20 text-amber-400'
869
+ : 'border-zinc-700 text-zinc-500 hover:text-zinc-300 hover:border-zinc-600'
870
+ }`}
871
+ title={showFiles ? 'Hide file explorer' : 'Show file explorer'}
872
+ >
873
+ <FolderOpen className="w-3.5 h-3.5" />
874
+ Files
875
+ </button>
876
+ {hasCollaboration && activeWorkspace && (
877
+ <button
878
+ onClick={async () => {
879
+ const newVal = !activeWorkspace.collaboration;
880
+ await fetch(api(`/api/workspaces/${activeWorkspace.id}`), {
881
+ method: 'PUT',
882
+ headers: { 'Content-Type': 'application/json' },
883
+ body: JSON.stringify({ collaboration: newVal }),
884
+ });
885
+ setActiveWorkspace(prev => prev ? { ...prev, collaboration: newVal } : prev);
886
+ setWorkspaces(prev => prev.map(w => w.id === activeWorkspace.id ? { ...w, collaboration: newVal } : w));
887
+
888
+ // Sync all agent panes in this workspace to match the new collaboration state
889
+ const agentPanes = panes.filter(p => p.workspaceId === activeWorkspace.id && p.agentType !== 'shell');
890
+ for (const p of agentPanes) {
891
+ if (p.isCollaborating !== newVal) {
892
+ await updatePane(p.id, { isCollaborating: newVal } as Partial<PaneData>);
893
+ }
894
+ }
895
+ }}
896
+ className={`flex items-center gap-1.5 px-2.5 py-1.5 text-xs rounded-md border transition-colors ${
897
+ activeWorkspace.collaboration
898
+ ? 'border-indigo-500/50 bg-indigo-600/20 text-indigo-400'
899
+ : 'border-zinc-700 text-zinc-500 hover:text-zinc-300 hover:border-zinc-600'
900
+ }`}
901
+ title={activeWorkspace.collaboration ? 'Collaboration enabled — click to disable' : 'Enable collaboration for this workspace'}
902
+ >
903
+ <Users className="w-3.5 h-3.5" />
904
+ Collab
905
+ </button>
906
+ )}
907
+ <button
908
+ onClick={closeWorkspace}
909
+ className="flex items-center gap-1.5 px-3 py-1.5 text-xs border border-zinc-700 text-zinc-400 rounded-md hover:text-white hover:border-zinc-600 transition-colors"
910
+ title="Close space and return home"
911
+ >
912
+ <XCircle className="w-3.5 h-3.5" />
913
+ Close
914
+ </button>
915
+ <button
916
+ onClick={() => setShowAdd(true)}
917
+ className="flex items-center gap-1.5 px-3 py-1.5 text-xs bg-indigo-600 text-white rounded-md hover:bg-indigo-500 transition-colors"
918
+ >
919
+ <Plus className="w-3.5 h-3.5" />
920
+ Add Pane
921
+ </button>
922
+ </div>
923
+ </div>
924
+
925
+ {/* Save-as dialog */}
926
+ {showSaveAs && (
927
+ <div className="border-b border-zinc-800 bg-zinc-900 px-4 py-3">
928
+ <div className="max-w-md flex items-center gap-2">
929
+ <Save className="w-4 h-4 text-indigo-400 flex-shrink-0" />
930
+ <input
931
+ autoFocus
932
+ type="text"
933
+ placeholder="New space name..."
934
+ value={saveAsName}
935
+ onChange={(e) => setSaveAsName(e.target.value)}
936
+ onKeyDown={(e) => e.key === 'Enter' && saveWorkspaceAs()}
937
+ className="flex-1 px-3 py-1.5 text-sm bg-zinc-800 border border-zinc-700 rounded-md focus:outline-none focus:border-indigo-500 text-white"
938
+ />
939
+ <ColorPicker value={saveAsColor} onChange={setSaveAsColor} />
940
+ <button
941
+ onClick={saveWorkspaceAs}
942
+ disabled={!saveAsName.trim()}
943
+ className="px-3 py-1.5 text-xs bg-indigo-600 text-white rounded-md hover:bg-indigo-500 disabled:opacity-50"
944
+ >
945
+ Save
946
+ </button>
947
+ <button
948
+ onClick={() => setShowSaveAs(false)}
949
+ className="p-1.5 text-zinc-400 hover:text-white"
950
+ >
951
+ <X className="w-4 h-4" />
952
+ </button>
953
+ </div>
954
+ </div>
955
+ )}
956
+
957
+ {/* Add pane dialog */}
958
+ {showAdd && (
959
+ <div className="border-b border-zinc-800 bg-zinc-900 px-4 py-4">
960
+ <div className="max-w-lg space-y-3">
961
+ <h3 className="text-sm font-semibold">Add Pane</h3>
962
+
963
+ {/* Agent type grid */}
964
+ <div>
965
+ <label className="text-[11px] text-zinc-400 mb-1.5 block">Agent</label>
966
+ <div className="grid grid-cols-3 gap-1.5">
967
+ {AGENT_LIST.map((agent) => (
968
+ <button
969
+ key={agent.id}
970
+ onClick={() => {
971
+ setNewAgentType(agent.id);
972
+ setNewAgentMode('new');
973
+ setSelectedSession(null);
974
+ setNewClaudeSession('');
975
+ }}
976
+ className={`flex items-center gap-2 px-3 py-2 rounded-md border text-left transition-colors ${
977
+ newAgentType === agent.id
978
+ ? 'border-indigo-500 bg-indigo-600/20 text-white'
979
+ : 'border-zinc-700 text-zinc-400 hover:text-white hover:border-zinc-600'
980
+ }`}
981
+ >
982
+ <span
983
+ className="w-2.5 h-2.5 rounded-full flex-shrink-0"
984
+ style={{ backgroundColor: agent.color }}
985
+ />
986
+ <div className="min-w-0">
987
+ <div className="text-xs font-medium truncate">{agent.name}</div>
988
+ <div className="text-[10px] text-zinc-500 truncate">{agent.description}</div>
989
+ </div>
990
+ </button>
991
+ ))}
992
+ </div>
993
+ </div>
994
+
995
+ {/* New / Resume toggle (only for agents that support resume) */}
996
+ {AGENT_TYPES[newAgentType]?.supportsResume && (
997
+ <div className="flex gap-2">
998
+ <button
999
+ onClick={() => { setNewAgentMode('new'); setSelectedSession(null); setNewClaudeSession(''); }}
1000
+ className={`px-3 py-1.5 text-xs rounded-md border transition-colors ${
1001
+ newAgentMode === 'new'
1002
+ ? 'bg-indigo-600 border-indigo-500 text-white'
1003
+ : 'border-zinc-700 text-zinc-400 hover:text-white hover:border-zinc-600'
1004
+ }`}
1005
+ >
1006
+ New Session
1007
+ </button>
1008
+ <button
1009
+ onClick={() => setNewAgentMode('resume')}
1010
+ className={`px-3 py-1.5 text-xs rounded-md border transition-colors ${
1011
+ newAgentMode === 'resume'
1012
+ ? 'bg-indigo-600 border-indigo-500 text-white'
1013
+ : 'border-zinc-700 text-zinc-400 hover:text-white hover:border-zinc-600'
1014
+ }`}
1015
+ >
1016
+ Resume Session
1017
+ </button>
1018
+ </div>
1019
+ )}
1020
+
1021
+ {/* Custom command input */}
1022
+ {newAgentType === 'custom' && (
1023
+ <input
1024
+ type="text"
1025
+ placeholder="Command to run (e.g. python agent.py)"
1026
+ value={newCustomCommand}
1027
+ onChange={(e) => setNewCustomCommand(e.target.value)}
1028
+ className="w-full px-3 py-2 text-sm bg-zinc-800 border border-zinc-700 rounded-md focus:outline-none focus:border-indigo-500 text-white font-mono"
1029
+ />
1030
+ )}
1031
+
1032
+ <NodeSelector value={newNodeId} onChange={setNewNodeId} />
1033
+
1034
+ <input
1035
+ type="text"
1036
+ placeholder="Title (optional)"
1037
+ value={newTitle}
1038
+ onChange={(e) => setNewTitle(e.target.value)}
1039
+ className="w-full px-3 py-2 text-sm bg-zinc-800 border border-zinc-700 rounded-md focus:outline-none focus:border-indigo-500 text-white"
1040
+ />
1041
+
1042
+ {newNodeId ? (
1043
+ <div>
1044
+ <label className="text-[11px] text-zinc-400 mb-1.5 block">Working directory (on remote node)</label>
1045
+ <input
1046
+ type="text"
1047
+ placeholder="e.g. /home/user/project or ~ for home"
1048
+ value={newCwd}
1049
+ onChange={(e) => setNewCwd(e.target.value)}
1050
+ className="w-full px-3 py-2 text-sm bg-zinc-800 border border-zinc-700 rounded-md focus:outline-none focus:border-indigo-500 text-white font-mono"
1051
+ />
1052
+ </div>
1053
+ ) : (
1054
+ <DevDirectoryPicker value={newCwd} onChange={setNewCwd} />
1055
+ )}
1056
+
1057
+ {/* Session picker for resume mode */}
1058
+ {AGENT_TYPES[newAgentType]?.supportsResume && newAgentMode === 'resume' && (
1059
+ <div ref={pickerRef} className="relative">
1060
+ {selectedSession ? (
1061
+ <div className="flex items-center gap-2 px-3 py-2 text-sm bg-zinc-800 border border-zinc-700 rounded-md">
1062
+ <MessageSquare className="w-3.5 h-3.5 text-indigo-400 flex-shrink-0" />
1063
+ <div className="flex-1 min-w-0">
1064
+ <div className="text-white truncate">
1065
+ {selectedSession.customName || selectedSession.firstPrompt?.slice(0, 60) || 'Untitled'}
1066
+ </div>
1067
+ <div className="text-[10px] text-zinc-500 flex items-center gap-2">
1068
+ <span>{selectedSession.projectName}</span>
1069
+ <span>{selectedSession.messageCount} msgs</span>
1070
+ <span className="font-mono">{selectedSession.sessionId.slice(0, 8)}...</span>
1071
+ </div>
1072
+ </div>
1073
+ <button
1074
+ onClick={() => { setSelectedSession(null); setShowSessionPicker(true); }}
1075
+ className="text-zinc-400 hover:text-white text-xs"
1076
+ >
1077
+ Change
1078
+ </button>
1079
+ </div>
1080
+ ) : (
1081
+ <div
1082
+ className="flex items-center gap-2 px-3 py-2 text-sm bg-zinc-800 border border-zinc-700 rounded-md cursor-text"
1083
+ onClick={() => setShowSessionPicker(true)}
1084
+ >
1085
+ <Search className="w-3.5 h-3.5 text-zinc-500" />
1086
+ <input
1087
+ type="text"
1088
+ placeholder="Search sessions by name, prompt, or project..."
1089
+ value={sessionSearch}
1090
+ onChange={(e) => { setSessionSearch(e.target.value); setShowSessionPicker(true); }}
1091
+ onFocus={() => setShowSessionPicker(true)}
1092
+ className="flex-1 bg-transparent focus:outline-none text-white placeholder:text-zinc-500"
1093
+ />
1094
+ {newCwd && (
1095
+ <button
1096
+ onClick={(e) => { e.stopPropagation(); setFilterByCwd(!filterByCwd); }}
1097
+ className={`flex-shrink-0 px-1.5 py-0.5 text-[10px] rounded border transition-colors ${
1098
+ filterByCwd
1099
+ ? 'border-indigo-500/50 bg-indigo-600/20 text-indigo-400'
1100
+ : 'border-zinc-600 text-zinc-500 hover:text-zinc-400'
1101
+ }`}
1102
+ title={filterByCwd ? 'Showing sessions for this directory — click to show all' : 'Showing all sessions — click to filter by directory'}
1103
+ >
1104
+ {filterByCwd ? newCwd.split('/').pop() : 'All'}
1105
+ </button>
1106
+ )}
1107
+ <ChevronDown className="w-3.5 h-3.5 text-zinc-500" />
1108
+ </div>
1109
+ )}
1110
+
1111
+ {showSessionPicker && !selectedSession && (
1112
+ <div className="absolute z-50 top-full left-0 right-0 mt-1 max-h-[300px] overflow-y-auto bg-zinc-800 border border-zinc-700 rounded-md shadow-xl">
1113
+ {sessionsLoading ? (
1114
+ <div className="flex items-center justify-center py-6">
1115
+ <Loader2 className="w-4 h-4 animate-spin text-zinc-400" />
1116
+ <span className="text-xs text-zinc-400 ml-2">Loading sessions...</span>
1117
+ </div>
1118
+ ) : sessions.length === 0 ? (
1119
+ <div className="py-4 px-3 text-xs text-zinc-500 text-center">
1120
+ {sessionSearch ? 'No sessions match your search' : 'No sessions found'}
1121
+ </div>
1122
+ ) : (
1123
+ sessions.map((s) => (
1124
+ <button
1125
+ key={s.sessionId}
1126
+ onClick={() => {
1127
+ setSelectedSession(s);
1128
+ setNewClaudeSession(s.sessionId);
1129
+ if (!newCwd && s.projectPath) setNewCwd(s.projectPath);
1130
+ if (!newTitle) setNewTitle(s.customName || '');
1131
+ setShowSessionPicker(false);
1132
+ }}
1133
+ className="w-full text-left px-3 py-2.5 hover:bg-zinc-700 border-b border-zinc-700/50 last:border-0 transition-colors"
1134
+ >
1135
+ <div className="flex items-start gap-2">
1136
+ <MessageSquare className="w-3.5 h-3.5 text-indigo-400 mt-0.5 flex-shrink-0" />
1137
+ <div className="flex-1 min-w-0">
1138
+ <div className="text-xs text-white truncate">
1139
+ {s.customName || s.firstPrompt?.slice(0, 70) || 'Untitled session'}
1140
+ </div>
1141
+ <div className="flex items-center gap-2 mt-0.5">
1142
+ <span className="text-[10px] text-indigo-400/70 flex items-center gap-0.5">
1143
+ <FolderOpen className="w-2.5 h-2.5" />
1144
+ {s.projectName}
1145
+ </span>
1146
+ <span className="text-[10px] text-zinc-500">{s.messageCount} msgs</span>
1147
+ <span className="text-[10px] text-zinc-500 flex items-center gap-0.5">
1148
+ <Clock className="w-2.5 h-2.5" />
1149
+ {new Date(s.modified).toLocaleDateString()}
1150
+ </span>
1151
+ {s.starred && <span className="text-[10px]">★</span>}
1152
+ </div>
1153
+ {s.tags?.length > 0 && (
1154
+ <div className="flex gap-1 mt-1">
1155
+ {s.tags.map(t => (
1156
+ <span key={t} className="text-[9px] px-1.5 py-0.5 bg-zinc-700 rounded text-zinc-400">{t}</span>
1157
+ ))}
1158
+ </div>
1159
+ )}
1160
+ </div>
1161
+ </div>
1162
+ </button>
1163
+ ))
1164
+ )}
1165
+ </div>
1166
+ )}
1167
+ </div>
1168
+ )}
1169
+
1170
+ <div>
1171
+ <label className="text-[11px] text-zinc-400 mb-1.5 block">Color</label>
1172
+ <ColorPicker value={newColor} onChange={setNewColor} />
1173
+ </div>
1174
+
1175
+ <div className="flex gap-2 pt-1">
1176
+ <button
1177
+ onClick={addPane}
1178
+ disabled={newAgentType === 'custom' && !newCustomCommand.trim()}
1179
+ className="px-4 py-2 text-sm bg-indigo-600 text-white rounded-md hover:bg-indigo-500 disabled:opacity-50 disabled:cursor-not-allowed"
1180
+ >
1181
+ Create
1182
+ </button>
1183
+ <button
1184
+ onClick={() => setShowAdd(false)}
1185
+ className="px-4 py-2 text-sm border border-zinc-700 text-zinc-400 rounded-md hover:text-white hover:border-zinc-600"
1186
+ >
1187
+ Cancel
1188
+ </button>
1189
+ </div>
1190
+ </div>
1191
+ </div>
1192
+ )}
1193
+
1194
+ {/* Popped-out pane indicators */}
1195
+ {poppedOut.size > 0 && (
1196
+ <div className="flex items-center gap-2 px-4 py-1.5 border-b border-zinc-800 bg-zinc-900/50">
1197
+ <span className="text-[10px] text-zinc-500">Popped out:</span>
1198
+ {panes.filter(p => poppedOut.has(p.id)).map(p => (
1199
+ <div key={p.id} className="flex items-center gap-0.5">
1200
+ <button
1201
+ onClick={() => openPopoutWindow(p)}
1202
+ className="flex items-center gap-1 px-2 py-0.5 text-[10px] bg-zinc-800 border border-zinc-700 rounded-l text-zinc-400 hover:text-white hover:border-zinc-600"
1203
+ title="Focus popout window"
1204
+ >
1205
+ <span className="w-1.5 h-1.5 rounded-full" style={{ backgroundColor: p.color }} />
1206
+ {p.title}
1207
+ </button>
1208
+ <button
1209
+ onClick={() => popIn(p.id)}
1210
+ className="px-1 py-0.5 text-[10px] bg-zinc-800 border border-zinc-700 border-l-0 rounded-r text-zinc-500 hover:text-white hover:border-zinc-600"
1211
+ title="Pop back into grid"
1212
+ >
1213
+ <ArrowLeftToLine className="w-2.5 h-2.5" />
1214
+ </button>
1215
+ </div>
1216
+ ))}
1217
+ </div>
1218
+ )}
1219
+
1220
+ {/* File Explorer + Terminal grid + Activity Panel */}
1221
+ <div className="flex-1 flex overflow-hidden min-h-0">
1222
+ {showFiles && (
1223
+ <div className="w-64 flex-shrink-0">
1224
+ <FileExplorer
1225
+ onClose={() => setShowFiles(false)}
1226
+ navigateTo={browseTarget}
1227
+ />
1228
+ </div>
1229
+ )}
1230
+ {visiblePanes.length === 0 && poppedOut.size === 0 && minimized.size === 0 ? (
1231
+ <div className="flex-1 flex items-center justify-center">
1232
+ <div className="text-center space-y-3">
1233
+ {/* eslint-disable-next-line @next/next/no-img-element */}
1234
+ <img src={`${basePath}/spaces_icon.png`} alt="Spaces" className="w-16 h-16 mx-auto opacity-30" />
1235
+ <p className="text-zinc-500">No panes yet.</p>
1236
+ <p className="text-zinc-600 text-xs">Add a pane to start a shell, Claude, Codex, Gemini, or any agent.</p>
1237
+ <button
1238
+ onClick={() => setShowAdd(true)}
1239
+ className="px-4 py-2 text-sm bg-indigo-600 text-white rounded-md hover:bg-indigo-500 inline-flex items-center gap-2"
1240
+ >
1241
+ <Plus className="w-4 h-4" />
1242
+ Add your first pane
1243
+ </button>
1244
+ </div>
1245
+ </div>
1246
+ ) : visiblePanes.length === 0 ? (
1247
+ <div className="flex-1 flex items-center justify-center">
1248
+ <div className="text-center space-y-2">
1249
+ <p className="text-zinc-500 text-sm">
1250
+ {minimized.size > 0 && poppedOut.size > 0
1251
+ ? 'All panes are minimized or popped out.'
1252
+ : minimized.size > 0
1253
+ ? `All panes are minimized. Use the toolbar to restore them.`
1254
+ : 'All panes are popped out to separate windows.'}
1255
+ </p>
1256
+ <button
1257
+ onClick={() => setShowAdd(true)}
1258
+ className="px-3 py-1.5 text-xs text-zinc-400 hover:text-white border border-zinc-700 rounded-md hover:border-zinc-600 inline-flex items-center gap-1.5"
1259
+ >
1260
+ <Plus className="w-3.5 h-3.5" />
1261
+ Add another pane
1262
+ </button>
1263
+ </div>
1264
+ </div>
1265
+ ) : (
1266
+ <div className="flex-1 flex flex-col overflow-hidden min-h-0">
1267
+ <DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
1268
+ <SortableContext items={visiblePanes.map(p => p.id)} strategy={verticalListSortingStrategy}>
1269
+ <ResizablePaneGrid
1270
+ panes={maximized ? visiblePanes.filter(p => p.id === maximized) : visiblePanes}
1271
+ renderPane={(pane, dragHandleProps) => (
1272
+ <TerminalPane
1273
+ pane={pane}
1274
+ onClose={closePane}
1275
+ onUpdate={updatePane}
1276
+ isMaximized={maximized === pane.id}
1277
+ onToggleMaximize={toggleMaximize}
1278
+ onMinimize={minimizePane}
1279
+ onPopout={handlePopout}
1280
+ onBrowse={handleBrowse}
1281
+ terminalToken={terminalToken}
1282
+ workspaceCollaboration={activeWorkspace?.collaboration}
1283
+ dragHandleProps={dragHandleProps}
1284
+ />
1285
+ )}
1286
+ />
1287
+ </SortableContext>
1288
+ </DndContext>
1289
+
1290
+ {/* Minimized panes dock */}
1291
+ {minimizedPanes.length > 0 && (
1292
+ <div className="flex items-center gap-1.5 px-2 py-1.5 border-t border-zinc-800 flex-shrink-0 overflow-x-auto bg-zinc-950">
1293
+ {minimizedPanes.map(pane => (
1294
+ <button
1295
+ key={pane.id}
1296
+ onClick={() => restorePane(pane.id)}
1297
+ className="flex items-center gap-1.5 px-2.5 py-1 text-[11px] rounded-md border border-zinc-700 hover:border-zinc-500 bg-zinc-800/50 hover:bg-zinc-700/50 text-zinc-400 hover:text-white transition-colors flex-shrink-0"
1298
+ title={`Restore ${pane.title}`}
1299
+ >
1300
+ <span className="w-2 h-2 rounded-full flex-shrink-0" style={{ backgroundColor: pane.color }} />
1301
+ <span className="truncate max-w-[100px]">{pane.title}</span>
1302
+ <Maximize2 className="w-2.5 h-2.5 text-zinc-500" />
1303
+ </button>
1304
+ ))}
1305
+ </div>
1306
+ )}
1307
+ </div>
1308
+ )}
1309
+
1310
+ {activeWorkspace?.collaboration && (
1311
+ <ActivityPanel
1312
+ workspaceId={activeWorkspace?.id ?? null}
1313
+ panes={visiblePanes.map(p => ({ id: p.id, title: p.title, agentType: p.agentType, color: p.color }))}
1314
+ collapsed={busPanelCollapsed}
1315
+ onToggle={() => setBusPanelCollapsed(!busPanelCollapsed)}
1316
+ />
1317
+ )}
1318
+ </div>
1319
+
1320
+ <ProjectWizard
1321
+ isOpen={showWizard}
1322
+ onClose={() => setShowWizard(false)}
1323
+ onLaunch={(wsId) => {
1324
+ setShowWizard(false);
1325
+ switchWorkspace(wsId);
1326
+ }}
1327
+ />
1328
+ </div>
1329
+ );
1330
+ }