@shepai/cli 1.122.2 → 1.123.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. package/dist/src/presentation/web/app/(dashboard)/get-graph-data.d.ts +6 -0
  2. package/dist/src/presentation/web/app/(dashboard)/get-graph-data.d.ts.map +1 -1
  3. package/dist/src/presentation/web/app/(dashboard)/get-graph-data.js +60 -0
  4. package/dist/src/presentation/web/app/actions/get-feature-phase-timings.d.ts +2 -0
  5. package/dist/src/presentation/web/app/actions/get-feature-phase-timings.d.ts.map +1 -1
  6. package/dist/src/presentation/web/app/actions/get-feature-phase-timings.js +2 -0
  7. package/dist/src/presentation/web/app/build-graph-nodes.d.ts +7 -0
  8. package/dist/src/presentation/web/app/build-graph-nodes.d.ts.map +1 -1
  9. package/dist/src/presentation/web/app/build-graph-nodes.js +7 -0
  10. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.d.ts.map +1 -1
  11. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.js +20 -9
  12. package/dist/src/presentation/web/components/common/feature-drawer-tabs/activity-tab.d.ts.map +1 -1
  13. package/dist/src/presentation/web/components/common/feature-drawer-tabs/activity-tab.js +130 -31
  14. package/dist/src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.d.ts.map +1 -1
  15. package/dist/src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.js +13 -0
  16. package/dist/src/presentation/web/components/common/feature-drawer-tabs/use-tab-data-fetch.d.ts.map +1 -1
  17. package/dist/src/presentation/web/components/common/feature-drawer-tabs/use-tab-data-fetch.js +18 -9
  18. package/dist/src/presentation/web/components/common/repository-node/repository-node-config.d.ts +8 -0
  19. package/dist/src/presentation/web/components/common/repository-node/repository-node-config.d.ts.map +1 -1
  20. package/dist/src/presentation/web/components/common/repository-node/repository-node.d.ts.map +1 -1
  21. package/dist/src/presentation/web/components/common/repository-node/repository-node.js +6 -6
  22. package/dist/src/presentation/web/components/common/repository-node/repository-node.stories.d.ts +2 -0
  23. package/dist/src/presentation/web/components/common/repository-node/repository-node.stories.d.ts.map +1 -1
  24. package/dist/src/presentation/web/components/common/repository-node/repository-node.stories.js +107 -17
  25. package/dist/src/presentation/web/components/features/control-center/control-center-empty-state.d.ts.map +1 -1
  26. package/dist/src/presentation/web/components/features/control-center/control-center-empty-state.js +18 -18
  27. package/dist/src/presentation/web/components/features/control-center/control-center-inner.d.ts.map +1 -1
  28. package/dist/src/presentation/web/components/features/control-center/control-center-inner.js +31 -17
  29. package/dist/src/presentation/web/components/features/control-center/welcome-agent-setup.d.ts.map +1 -1
  30. package/dist/src/presentation/web/components/features/control-center/welcome-agent-setup.js +29 -22
  31. package/dist/src/presentation/web/components/features/features-canvas/features-canvas.js +1 -1
  32. package/dist/src/presentation/web/components/layouts/app-shell/app-shell.d.ts.map +1 -1
  33. package/dist/src/presentation/web/components/layouts/app-shell/app-shell.js +2 -2
  34. package/dist/src/presentation/web/hooks/sidebar-features-context.d.ts +2 -0
  35. package/dist/src/presentation/web/hooks/sidebar-features-context.d.ts.map +1 -1
  36. package/dist/src/presentation/web/hooks/sidebar-features-context.js +2 -1
  37. package/dist/src/presentation/web/lib/layout-with-dagre.js +1 -1
  38. package/dist/tsconfig.build.tsbuildinfo +1 -1
  39. package/package.json +1 -1
  40. package/web/.next/BUILD_ID +1 -1
  41. package/web/.next/build-manifest.json +2 -2
  42. package/web/.next/fallback-build-manifest.json +2 -2
  43. package/web/.next/prerender-manifest.json +3 -3
  44. package/web/.next/required-server-files.js +2 -2
  45. package/web/.next/required-server-files.json +2 -2
  46. package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +22 -22
  47. package/web/.next/server/app/(dashboard)/@drawer/create/page.js +2 -2
  48. package/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
  49. package/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
  50. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page/server-reference-manifest.json +28 -28
  51. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page.js +3 -3
  52. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page.js.nft.json +1 -1
  53. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
  54. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +28 -28
  55. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js +3 -3
  56. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
  57. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
  58. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +20 -20
  59. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js +2 -2
  60. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
  61. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  62. package/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +22 -22
  63. package/web/.next/server/app/(dashboard)/create/page.js +2 -2
  64. package/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
  65. package/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
  66. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page/server-reference-manifest.json +28 -28
  67. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page.js +3 -3
  68. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page.js.nft.json +1 -1
  69. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
  70. package/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +28 -28
  71. package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js +3 -3
  72. package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
  73. package/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
  74. package/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +20 -20
  75. package/web/.next/server/app/(dashboard)/page.js +2 -2
  76. package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
  77. package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
  78. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +20 -20
  79. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js +2 -2
  80. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
  81. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  82. package/web/.next/server/app/_global-error.html +2 -2
  83. package/web/.next/server/app/_global-error.rsc +1 -1
  84. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  85. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  86. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  87. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  88. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  89. package/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  90. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  91. package/web/.next/server/app/api/graph-data/route.js +1 -1
  92. package/web/.next/server/app/api/graph-data/route.js.nft.json +1 -1
  93. package/web/.next/server/app/settings/page/server-reference-manifest.json +6 -6
  94. package/web/.next/server/app/settings/page.js.nft.json +1 -1
  95. package/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  96. package/web/.next/server/app/skills/page/server-reference-manifest.json +6 -6
  97. package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  98. package/web/.next/server/app/tools/page/server-reference-manifest.json +6 -6
  99. package/web/.next/server/app/tools/page.js.nft.json +1 -1
  100. package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  101. package/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
  102. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  103. package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_370c43b1.js +1 -1
  104. package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_370c43b1.js.map +1 -1
  105. package/web/.next/server/chunks/[root-of-the-server]__a402b567._.js +1 -1
  106. package/web/.next/server/chunks/[root-of-the-server]__dcd4f847._.js +3 -0
  107. package/web/.next/server/chunks/{[root-of-the-server]__42b7ab55._.js.map → [root-of-the-server]__dcd4f847._.js.map} +1 -1
  108. package/web/.next/server/chunks/ssr/403f9_next_dist_31ea46d3._.js +1 -1
  109. package/web/.next/server/chunks/ssr/403f9_next_dist_c9d9b4d7._.js +1 -1
  110. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
  111. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js.map +1 -1
  112. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_f30d0343.js +1 -1
  113. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_f30d0343.js.map +1 -1
  114. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_9f1775bb.js +1 -1
  115. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_9f1775bb.js.map +1 -1
  116. package/web/.next/server/chunks/ssr/[root-of-the-server]__06fef644._.js +1 -1
  117. package/web/.next/server/chunks/ssr/{[root-of-the-server]__a022445f._.js → [root-of-the-server]__14664e9b._.js} +3 -3
  118. package/web/.next/server/chunks/ssr/{[root-of-the-server]__a022445f._.js.map → [root-of-the-server]__14664e9b._.js.map} +1 -1
  119. package/web/.next/server/chunks/ssr/[root-of-the-server]__4993a47c._.js +3 -0
  120. package/web/.next/server/chunks/ssr/[root-of-the-server]__4993a47c._.js.map +1 -0
  121. package/web/.next/server/chunks/ssr/[root-of-the-server]__545792ab._.js +1 -1
  122. package/web/.next/server/chunks/ssr/[root-of-the-server]__545792ab._.js.map +1 -1
  123. package/web/.next/server/chunks/ssr/[root-of-the-server]__6677a82c._.js +3 -0
  124. package/web/.next/server/chunks/ssr/{[root-of-the-server]__b724df9e._.js.map → [root-of-the-server]__6677a82c._.js.map} +1 -1
  125. package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +1 -1
  126. package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js.map +1 -1
  127. package/web/.next/server/chunks/ssr/[root-of-the-server]__6bb00d90._.js +2 -2
  128. package/web/.next/server/chunks/ssr/[root-of-the-server]__6bb00d90._.js.map +1 -1
  129. package/web/.next/server/chunks/ssr/{[root-of-the-server]__8e3bf5d7._.js → [root-of-the-server]__7cbda1ea._.js} +2 -2
  130. package/web/.next/server/chunks/ssr/[root-of-the-server]__7cbda1ea._.js.map +1 -0
  131. package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js +1 -1
  132. package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js.map +1 -1
  133. package/web/.next/server/chunks/ssr/[root-of-the-server]__8c553503._.js +2 -2
  134. package/web/.next/server/chunks/ssr/{[root-of-the-server]__2698e9f4._.js → [root-of-the-server]__9e8b25ad._.js} +2 -2
  135. package/web/.next/server/chunks/ssr/[root-of-the-server]__9e8b25ad._.js.map +1 -0
  136. package/web/.next/server/chunks/ssr/[root-of-the-server]__a7de2711._.js +2 -2
  137. package/web/.next/server/chunks/ssr/{[root-of-the-server]__c07f9f79._.js → [root-of-the-server]__aed1d6f8._.js} +2 -2
  138. package/web/.next/server/chunks/ssr/[root-of-the-server]__aed1d6f8._.js.map +1 -0
  139. package/web/.next/server/chunks/ssr/{[root-of-the-server]__c605a375._.js → [root-of-the-server]__ce6057f6._.js} +2 -2
  140. package/web/.next/server/chunks/ssr/[root-of-the-server]__ce6057f6._.js.map +1 -0
  141. package/web/.next/server/chunks/ssr/_135edb9c._.js +1 -1
  142. package/web/.next/server/chunks/ssr/_135edb9c._.js.map +1 -1
  143. package/web/.next/server/chunks/ssr/_2f6f48b8._.js +1 -1
  144. package/web/.next/server/chunks/ssr/_2f6f48b8._.js.map +1 -1
  145. package/web/.next/server/chunks/ssr/_3a4c94e3._.js +1 -1
  146. package/web/.next/server/chunks/ssr/{_42caff2c._.js → _960ceb5d._.js} +2 -2
  147. package/web/.next/server/chunks/ssr/_960ceb5d._.js.map +1 -0
  148. package/web/.next/server/chunks/ssr/_b56e7571._.js +3 -0
  149. package/web/.next/server/chunks/ssr/_b56e7571._.js.map +1 -0
  150. package/web/.next/server/chunks/ssr/_cc77a143._.js +1 -1
  151. package/web/.next/server/chunks/ssr/_cc77a143._.js.map +1 -1
  152. package/web/.next/server/chunks/ssr/{_0dc2cfd3._.js → _e9703421._.js} +2 -2
  153. package/web/.next/server/chunks/ssr/_e9703421._.js.map +1 -0
  154. package/web/.next/server/chunks/ssr/_fd4c574b._.js +3 -0
  155. package/web/.next/server/chunks/ssr/_fd4c574b._.js.map +1 -0
  156. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js +1 -1
  157. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js.map +1 -1
  158. package/web/.next/server/chunks/ssr/node_modules__pnpm_bc66dcc6._.js +3 -0
  159. package/web/.next/server/chunks/ssr/node_modules__pnpm_bc66dcc6._.js.map +1 -0
  160. package/web/.next/server/chunks/ssr/src_presentation_web_5f039db1._.js +2 -2
  161. package/web/.next/server/chunks/ssr/src_presentation_web_5f039db1._.js.map +1 -1
  162. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_2a52b697.js +1 -1
  163. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_2a52b697.js.map +1 -1
  164. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1fa8042e.js +1 -1
  165. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1fa8042e.js.map +1 -1
  166. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_24684200.js +1 -1
  167. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_24684200.js.map +1 -1
  168. package/web/.next/server/chunks/ssr/src_presentation_web_c4337169._.js +2 -2
  169. package/web/.next/server/chunks/ssr/src_presentation_web_c4337169._.js.map +1 -1
  170. package/web/.next/server/chunks/ssr/src_presentation_web_ca99d62d._.js +1 -1
  171. package/web/.next/server/chunks/ssr/src_presentation_web_ca99d62d._.js.map +1 -1
  172. package/web/.next/server/chunks/ssr/src_presentation_web_components_bf50c527._.js +1 -1
  173. package/web/.next/server/chunks/ssr/src_presentation_web_components_bf50c527._.js.map +1 -1
  174. package/web/.next/server/chunks/ssr/src_presentation_web_components_de814675._.js +1 -1
  175. package/web/.next/server/chunks/ssr/src_presentation_web_components_de814675._.js.map +1 -1
  176. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +1 -1
  177. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js.map +1 -1
  178. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
  179. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js.map +1 -1
  180. package/web/.next/server/chunks/ssr/src_presentation_web_db779ef4._.js +3 -0
  181. package/web/.next/server/chunks/ssr/src_presentation_web_db779ef4._.js.map +1 -0
  182. package/web/.next/server/pages/500.html +2 -2
  183. package/web/.next/server/server-reference-manifest.js +1 -1
  184. package/web/.next/server/server-reference-manifest.json +34 -34
  185. package/web/.next/static/chunks/{0110425f3a60fc18.js → 310be54600c8ce20.js} +2 -2
  186. package/web/.next/static/chunks/{ff81a9dd7b7bc56f.js → 373e10090c6410c4.js} +1 -1
  187. package/web/.next/static/chunks/3925626b99dcd57a.css +1 -0
  188. package/web/.next/static/chunks/3f6c44cbad751310.js +1 -0
  189. package/web/.next/static/chunks/4443ee9c7733399b.js +1 -0
  190. package/web/.next/static/chunks/{f464a486bd88502d.js → 64d063449c3be0ec.js} +1 -1
  191. package/web/.next/static/chunks/694718e5449b3150.js +1 -0
  192. package/web/.next/static/chunks/{6339d1c357e7fea3.js → 878643f90bc6360f.js} +2 -2
  193. package/web/.next/static/chunks/94af5be1ccf8842e.js +1 -0
  194. package/web/.next/static/chunks/9821072252ddb89a.js +1 -0
  195. package/web/.next/static/chunks/ac7251026c1e2cdf.js +1 -0
  196. package/web/.next/static/chunks/{96eeeb28e06de78c.js → dbbb446493f21874.js} +1 -1
  197. package/web/.next/static/chunks/e418898e8e02f50a.js +1 -0
  198. package/web/.next/static/chunks/{17d618a2fb4e9a11.js → f0dfeedb263eb906.js} +1 -1
  199. package/web/.next/server/chunks/[root-of-the-server]__42b7ab55._.js +0 -3
  200. package/web/.next/server/chunks/ssr/[root-of-the-server]__2698e9f4._.js.map +0 -1
  201. package/web/.next/server/chunks/ssr/[root-of-the-server]__8e3bf5d7._.js.map +0 -1
  202. package/web/.next/server/chunks/ssr/[root-of-the-server]__9f155c0e._.js +0 -3
  203. package/web/.next/server/chunks/ssr/[root-of-the-server]__9f155c0e._.js.map +0 -1
  204. package/web/.next/server/chunks/ssr/[root-of-the-server]__b724df9e._.js +0 -3
  205. package/web/.next/server/chunks/ssr/[root-of-the-server]__c07f9f79._.js.map +0 -1
  206. package/web/.next/server/chunks/ssr/[root-of-the-server]__c605a375._.js.map +0 -1
  207. package/web/.next/server/chunks/ssr/_0dc2cfd3._.js.map +0 -1
  208. package/web/.next/server/chunks/ssr/_42caff2c._.js.map +0 -1
  209. package/web/.next/server/chunks/ssr/_75f4ccb3._.js +0 -3
  210. package/web/.next/server/chunks/ssr/_75f4ccb3._.js.map +0 -1
  211. package/web/.next/server/chunks/ssr/_9b7b1c2b._.js +0 -3
  212. package/web/.next/server/chunks/ssr/_9b7b1c2b._.js.map +0 -1
  213. package/web/.next/server/chunks/ssr/_ecfef26e._.js +0 -3
  214. package/web/.next/server/chunks/ssr/_ecfef26e._.js.map +0 -1
  215. package/web/.next/server/chunks/ssr/node_modules__pnpm_b59e4b16._.js +0 -3
  216. package/web/.next/server/chunks/ssr/node_modules__pnpm_b59e4b16._.js.map +0 -1
  217. package/web/.next/static/chunks/0d2af96e64960ef5.css +0 -1
  218. package/web/.next/static/chunks/12f3ae6b693c8baf.js +0 -1
  219. package/web/.next/static/chunks/4562ee849ea13c3a.js +0 -1
  220. package/web/.next/static/chunks/83fed73369b40eaf.js +0 -1
  221. package/web/.next/static/chunks/845d8e58a91014e6.js +0 -1
  222. package/web/.next/static/chunks/8e5bdf9fb945ae6a.js +0 -1
  223. package/web/.next/static/chunks/be0d245d0e672d44.js +0 -1
  224. package/web/.next/static/chunks/e6c0bf49bcd5d0fb.js +0 -1
  225. /package/web/.next/static/{DIjy_LTLHBNoz5RRTRhBy → gBTe97SiNDA2hbG9dbfpb}/_buildManifest.js +0 -0
  226. /package/web/.next/static/{DIjy_LTLHBNoz5RRTRhBy → gBTe97SiNDA2hbG9dbfpb}/_clientMiddlewareManifest.json +0 -0
  227. /package/web/.next/static/{DIjy_LTLHBNoz5RRTRhBy → gBTe97SiNDA2hbG9dbfpb}/_ssgManifest.js +0 -0
@@ -54,11 +54,16 @@ export function useTabDataFetch(featureId, fetchers) {
54
54
  const currentFeatureId = featureIdRef.current;
55
55
  if (!fetcher)
56
56
  return;
57
+ // Only show loading spinner when there's no existing data.
58
+ // On refresh (data already present), keep showing stale data while fetching
59
+ // to avoid flickering the entire tab on every poll cycle.
57
60
  if (mountedRef.current) {
58
- setTabStates((prev) => ({
59
- ...prev,
60
- [tab]: { ...prev[tab], loading: true, error: null },
61
- }));
61
+ setTabStates((prev) => {
62
+ const hasData = prev[tab]?.data !== null;
63
+ if (hasData)
64
+ return prev; // Keep stale data visible — no loading flash
65
+ return { ...prev, [tab]: { ...prev[tab], loading: true, error: null } };
66
+ });
62
67
  }
63
68
  try {
64
69
  const data = await fetcher(currentFeatureId);
@@ -71,11 +76,15 @@ export function useTabDataFetch(featureId, fetchers) {
71
76
  }
72
77
  catch (error) {
73
78
  if (mountedRef.current) {
74
- const message = error instanceof Error ? error.message : 'Failed to fetch tab data';
75
- setTabStates((prev) => ({
76
- ...prev,
77
- [tab]: { data: null, loading: false, error: message },
78
- }));
79
+ setTabStates((prev) => {
80
+ const message = error instanceof Error ? error.message : 'Failed to fetch tab data';
81
+ // On refresh failure, keep existing data instead of clearing it
82
+ const existingData = prev[tab]?.data ?? null;
83
+ return {
84
+ ...prev,
85
+ [tab]: { data: existingData, loading: false, error: existingData ? null : message },
86
+ };
87
+ });
79
88
  }
80
89
  }
81
90
  }, []);
@@ -9,6 +9,14 @@ export interface RepositoryNodeData {
9
9
  repositoryPath?: string;
10
10
  /** Creation timestamp (epoch ms) — used for stable vertical ordering. */
11
11
  createdAt?: number;
12
+ /** Current git branch name (e.g. "main", "feat/my-feature") */
13
+ branch?: string;
14
+ /** Commit message of the branch HEAD (first line only) */
15
+ commitMessage?: string;
16
+ /** Name of the person who made the latest commit */
17
+ committer?: string;
18
+ /** Number of commits the current branch is behind the default branch. null if on default branch or unknown. */
19
+ behindCount?: number | null;
12
20
  onClick?: () => void;
13
21
  onAdd?: () => void;
14
22
  onDelete?: (repositoryId: string) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"repository-node-config.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/repository-node/repository-node-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAE1C,MAAM,WAAW,kBAAkB;IACjC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,qEAAqE;IACrE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;IACnB,QAAQ,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,kBAAkB,EAAE,gBAAgB,CAAC,CAAC"}
1
+ {"version":3,"file":"repository-node-config.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/repository-node/repository-node-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAE1C,MAAM,WAAW,kBAAkB;IACjC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,qEAAqE;IACrE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oDAAoD;IACpD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+GAA+G;IAC/G,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;IACnB,QAAQ,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,kBAAkB,EAAE,gBAAgB,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"repository-node.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/repository-node/repository-node.tsx"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAOnE,wBAAgB,cAAc,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,2CAyR5F"}
1
+ {"version":3,"file":"repository-node.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/repository-node/repository-node.tsx"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAOnE,wBAAgB,cAAc,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,2CA+U5F"}
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  import { useState, useCallback } from 'react';
4
4
  import { Handle, Position } from '@xyflow/react';
5
5
  import { useRouter } from 'next/navigation';
6
- import { Github, Plus, Code2, Terminal, FolderOpen, Trash2, Play, Square } from 'lucide-react';
6
+ import { Github, Plus, Code2, Terminal, FolderOpen, Trash2, Play, Square, GitBranch, GitCommitHorizontal, ArrowDown, User, } from 'lucide-react';
7
7
  import { cn } from '../../../lib/utils.js';
8
8
  import { ActionButton } from '../../common/action-button/index.js';
9
9
  import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '../../ui/dialog.js';
@@ -72,9 +72,9 @@ export function RepositoryNode({ data }) {
72
72
  e.stopPropagation();
73
73
  data.onClick?.();
74
74
  }
75
- }, className: "nodrag bg-card flex min-w-[18rem] cursor-pointer items-center gap-3 rounded-full border px-4 py-3 shadow-sm", children: [_jsx(Github, { className: "text-muted-foreground h-5 w-5 shrink-0" }), _jsx("span", { "data-testid": "repository-node-name", className: "min-w-0 truncate text-sm font-medium", children: data.name }), _jsxs("div", { className: cn('flex shrink-0 items-center gap-2', (data.repositoryPath ?? data.onAdd) && 'ml-auto'), onClick: (e) => e.stopPropagation(), children: [data.repositoryPath ? (_jsxs(_Fragment, { children: [_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: "flex items-center", children: _jsx(ActionButton, { label: "Open in IDE", onClick: actions.openInIde, loading: actions.ideLoading, error: !!actions.ideError, icon: Code2, iconOnly: true, variant: "ghost", size: "icon-xs" }) }) }), _jsx(TooltipContent, { children: "Open in IDE" })] }) }), _jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: "flex items-center", children: _jsx(ActionButton, { label: "Open in Shell", onClick: actions.openInShell, loading: actions.shellLoading, error: !!actions.shellError, icon: Terminal, iconOnly: true, variant: "ghost", size: "icon-xs" }) }) }), _jsx(TooltipContent, { children: "Open in Shell" })] }) }), _jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: "flex items-center", children: _jsx(ActionButton, { label: "Open Folder", onClick: actions.openFolder, loading: actions.folderLoading, error: !!actions.folderError, icon: FolderOpen, iconOnly: true, variant: "ghost", size: "icon-xs" }) }) }), _jsx(TooltipContent, { children: "Open Folder" })] }) }), _jsx(FeatureSessionsDropdown, { repositoryPath: data.repositoryPath, onCreateFromSession: handleCreateFromSession }), featureFlags.envDeploy ? (_jsxs(_Fragment, { children: [_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: "flex items-center", children: _jsx(ActionButton, { label: isDeploymentActive ? 'Stop Dev Server' : 'Start Dev Server', onClick: isDeploymentActive ? deployAction.stop : deployAction.deploy, loading: deployAction.deployLoading || deployAction.stopLoading, error: !!deployAction.deployError, icon: isDeploymentActive ? Square : Play, iconOnly: true, variant: "ghost", size: "icon-xs" }) }) }), _jsx(TooltipContent, { children: isDeploymentActive ? 'Stop Dev Server' : 'Start Dev Server' })] }) }), isDeploymentActive ? (_jsx(DeploymentStatusBadge, { status: deployAction.status, url: deployAction.url, targetId: data.repositoryPath })) : null] })) : null] })) : null, data.onAdd ? (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { "aria-label": "Add feature", "data-testid": "repository-node-add-button", onClick: (e) => {
76
- e.stopPropagation();
77
- data.onAdd?.();
78
- }, className: cn('text-muted-foreground hover:bg-accent dark:hover:bg-accent/50 flex h-6 w-6 shrink-0 cursor-pointer items-center justify-center rounded-full transition-colors hover:text-blue-500', data.pulseAdd &&
79
- 'animate-pulse-cta bg-blue-100 text-blue-500 dark:bg-blue-900/40'), children: _jsx(Plus, { className: "h-3.5 w-3.5" }) }) }), _jsx(TooltipContent, { children: "Add feature" })] }) })) : null] })] }), data.onAdd || data.showHandles ? (_jsx(Handle, { type: "source", position: Position.Right, isConnectable: !data.showHandles, className: "opacity-0!" })) : null] }));
75
+ }, className: "nodrag bg-card flex w-[26rem] cursor-pointer flex-col overflow-hidden rounded-xl border shadow-sm", children: [_jsxs("div", { className: "flex items-center gap-3 px-4 py-3", children: [_jsx(Github, { className: "text-muted-foreground h-5 w-5 shrink-0" }), _jsx("span", { "data-testid": "repository-node-name", className: "min-w-0 truncate text-sm font-medium", children: data.name }), _jsxs("div", { className: cn('flex shrink-0 items-center gap-2', (data.repositoryPath ?? data.onAdd) && 'ml-auto'), onClick: (e) => e.stopPropagation(), children: [data.repositoryPath ? (_jsxs(_Fragment, { children: [_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: "flex items-center", children: _jsx(ActionButton, { label: "Open in IDE", onClick: actions.openInIde, loading: actions.ideLoading, error: !!actions.ideError, icon: Code2, iconOnly: true, variant: "ghost", size: "icon-xs" }) }) }), _jsx(TooltipContent, { children: "Open in IDE" })] }) }), _jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: "flex items-center", children: _jsx(ActionButton, { label: "Open in Shell", onClick: actions.openInShell, loading: actions.shellLoading, error: !!actions.shellError, icon: Terminal, iconOnly: true, variant: "ghost", size: "icon-xs" }) }) }), _jsx(TooltipContent, { children: "Open in Shell" })] }) }), _jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: "flex items-center", children: _jsx(ActionButton, { label: "Open Folder", onClick: actions.openFolder, loading: actions.folderLoading, error: !!actions.folderError, icon: FolderOpen, iconOnly: true, variant: "ghost", size: "icon-xs" }) }) }), _jsx(TooltipContent, { children: "Open Folder" })] }) }), _jsx(FeatureSessionsDropdown, { repositoryPath: data.repositoryPath, onCreateFromSession: handleCreateFromSession }), featureFlags.envDeploy ? (_jsxs(_Fragment, { children: [_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: "flex items-center", children: _jsx(ActionButton, { label: isDeploymentActive ? 'Stop Dev Server' : 'Start Dev Server', onClick: isDeploymentActive ? deployAction.stop : deployAction.deploy, loading: deployAction.deployLoading || deployAction.stopLoading, error: !!deployAction.deployError, icon: isDeploymentActive ? Square : Play, iconOnly: true, variant: "ghost", size: "icon-xs" }) }) }), _jsx(TooltipContent, { children: isDeploymentActive ? 'Stop Dev Server' : 'Start Dev Server' })] }) }), isDeploymentActive ? (_jsx(DeploymentStatusBadge, { status: deployAction.status, url: deployAction.url, targetId: data.repositoryPath })) : null] })) : null] })) : null, data.onAdd ? (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { "aria-label": "Add feature", "data-testid": "repository-node-add-button", onClick: (e) => {
76
+ e.stopPropagation();
77
+ data.onAdd?.();
78
+ }, className: cn('text-muted-foreground hover:bg-accent dark:hover:bg-accent/50 flex h-6 w-6 shrink-0 cursor-pointer items-center justify-center rounded-full transition-colors hover:text-blue-500', data.pulseAdd &&
79
+ 'animate-pulse-cta bg-blue-100 text-blue-500 dark:bg-blue-900/40'), children: _jsx(Plus, { className: "h-3.5 w-3.5" }) }) }), _jsx(TooltipContent, { children: "Add feature" })] }) })) : null] })] }), data.branch ? (_jsx("div", { "data-testid": "repository-node-git-info", className: "text-muted-foreground border-t px-4 py-2", children: _jsxs("div", { className: "flex items-center gap-3 text-xs", children: [_jsxs("span", { className: "flex items-center gap-1 truncate", "data-testid": "repository-node-branch", children: [_jsx(GitBranch, { className: "h-3 w-3 shrink-0" }), _jsx("span", { className: "truncate", children: data.branch })] }), data.behindCount != null && data.behindCount > 0 ? (_jsxs("span", { className: "flex shrink-0 items-center gap-1 whitespace-nowrap text-amber-500", "data-testid": "repository-node-behind", children: [_jsx(ArrowDown, { className: "h-3 w-3 shrink-0" }), data.behindCount, " behind"] })) : null] }) })) : null, data.commitMessage ? (_jsx("div", { "data-testid": "repository-node-commit-info", className: "text-muted-foreground border-t px-4 py-2", children: _jsxs("div", { className: "flex items-center gap-2 text-xs", children: [_jsx(GitCommitHorizontal, { className: "h-3 w-3 shrink-0" }), _jsx("span", { className: "min-w-0 truncate", "data-testid": "repository-node-commit-message", children: data.commitMessage }), data.committer ? (_jsxs("span", { className: "text-muted-foreground/70 ml-auto flex shrink-0 items-center gap-1", "data-testid": "repository-node-committer", children: [_jsx(User, { className: "h-3 w-3 shrink-0" }), _jsx("span", { children: data.committer })] })) : null] }) })) : null] }), data.onAdd || data.showHandles ? (_jsx(Handle, { type: "source", position: Position.Right, isConnectable: !data.showHandles, className: "opacity-0!" })) : null] }));
80
80
  }
@@ -14,4 +14,6 @@ export declare const WithActions: Story;
14
14
  export declare const WithActionsAndAddButton: Story;
15
15
  export declare const WithDeleteButton: Story;
16
16
  export declare const MultipleWithActions: Story;
17
+ export declare const WithGitInfo: Story;
18
+ export declare const WithGitInfoBehind: Story;
17
19
  //# sourceMappingURL=repository-node.stories.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"repository-node.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/repository-node/repository-node.stories.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAGvD,OAAO,KAAK,EAAE,kBAAkB,EAAsB,MAAM,0BAA0B,CAAC;AAgCvF,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,kBAAkB,CASlC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,kBAAkB,CAAC,CAAC;AAE1C,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,KAK3B,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,KAKzB,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAKtB,CAAC;AAWF,eAAO,MAAM,QAAQ,EAAE,KAwBtB,CAAC;AASF,eAAO,MAAM,kBAAkB,EAAE,KAwBhC,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,KAS1B,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,KAKzB,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,KAQrC,CAAC;AAIF,eAAO,MAAM,gBAAgB,EAAE,KAS9B,CAAC;AA2BF,eAAO,MAAM,mBAAmB,EAAE,KAwBjC,CAAC"}
1
+ {"version":3,"file":"repository-node.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/repository-node/repository-node.stories.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAGvD,OAAO,KAAK,EAAE,kBAAkB,EAAsB,MAAM,0BAA0B,CAAC;AAgCvF,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,kBAAkB,CAYlC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,kBAAkB,CAAC,CAAC;AAE1C,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,KAK3B,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,KAKzB,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAKtB,CAAC;AAiCF,eAAO,MAAM,QAAQ,EAAE,KAwBtB,CAAC;AAkCF,eAAO,MAAM,kBAAkB,EAAE,KAwBhC,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,KAS1B,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,KAKzB,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,KAQrC,CAAC;AAIF,eAAO,MAAM,gBAAgB,EAAE,KAS9B,CAAC;AA+CF,eAAO,MAAM,mBAAmB,EAAE,KAwBjC,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,KASzB,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,KAS/B,CAAC"}
@@ -3,7 +3,7 @@ import { useMemo } from 'react';
3
3
  import { ReactFlowProvider, ReactFlow } from '@xyflow/react';
4
4
  import { RepositoryNode } from './repository-node.js';
5
5
  const nodeTypes = { repositoryNode: RepositoryNode };
6
- function RepositoryNodeCanvas({ data, style = { width: 500, height: 250 }, }) {
6
+ function RepositoryNodeCanvas({ data, style = { width: 550, height: 300 }, }) {
7
7
  const nodes = useMemo(() => [{ id: 'node-1', type: 'repositoryNode', position: { x: 0, y: 0 }, data }], [data]);
8
8
  return (_jsx("div", { style: style, children: _jsx(ReactFlowProvider, { children: _jsx(ReactFlow, { nodes: nodes, nodeTypes: nodeTypes, nodesDraggable: false, nodesConnectable: false, proOptions: { hideAttribution: true }, fitView: true }) }) }));
9
9
  }
@@ -15,6 +15,9 @@ const meta = {
15
15
  },
16
16
  args: {
17
17
  name: 'shep-ai/cli',
18
+ branch: 'main',
19
+ commitMessage: 'feat: add dark mode toggle',
20
+ committer: 'Jane Doe',
18
21
  },
19
22
  };
20
23
  export default meta;
@@ -41,37 +44,84 @@ export const LongName = {
41
44
  };
42
45
  const onAdd = () => undefined;
43
46
  const multipleRepos = [
44
- { name: 'shep-ai/cli' },
45
- { name: 'vercel/edge-runtime' },
46
- { name: 'acme-corp/web-dashboard-v2' },
47
- { name: 'openai/tiktoken' },
47
+ {
48
+ name: 'shep-ai/cli',
49
+ branch: 'main',
50
+ commitMessage: 'feat: add dark mode toggle',
51
+ committer: 'Jane Doe',
52
+ },
53
+ {
54
+ name: 'vercel/edge-runtime',
55
+ branch: 'feat/streaming',
56
+ commitMessage: 'fix: handle edge cases in streaming',
57
+ committer: 'John Smith',
58
+ behindCount: 3,
59
+ },
60
+ {
61
+ name: 'acme-corp/web-dashboard-v2',
62
+ branch: 'develop',
63
+ commitMessage: 'chore: update dependencies',
64
+ committer: 'Bot',
65
+ behindCount: 12,
66
+ },
67
+ {
68
+ name: 'openai/tiktoken',
69
+ branch: 'main',
70
+ commitMessage: 'perf: optimize token encoding',
71
+ committer: 'Alice Chen',
72
+ },
48
73
  ];
49
74
  export const Multiple = {
50
75
  render: () => {
51
76
  const nodes = multipleRepos.map((data, i) => ({
52
77
  id: `repo-${i}`,
53
78
  type: 'repositoryNode',
54
- position: { x: 0, y: i * 80 },
79
+ position: { x: 0, y: i * 140 },
55
80
  data,
56
81
  }));
57
- return (_jsx("div", { style: { width: 500, height: 500 }, children: _jsx(ReactFlowProvider, { children: _jsx(ReactFlow, { nodes: nodes, nodeTypes: nodeTypes, nodesDraggable: false, nodesConnectable: false, proOptions: { hideAttribution: true }, fitView: true }) }) }));
82
+ return (_jsx("div", { style: { width: 600, height: 750 }, children: _jsx(ReactFlowProvider, { children: _jsx(ReactFlow, { nodes: nodes, nodeTypes: nodeTypes, nodesDraggable: false, nodesConnectable: false, proOptions: { hideAttribution: true }, fitView: true }) }) }));
58
83
  },
59
84
  };
60
85
  const multipleReposWithButton = [
61
- { name: 'shep-ai/cli', onAdd },
62
- { name: 'vercel/edge-runtime', onAdd },
63
- { name: 'acme-corp/web-dashboard-v2', onAdd },
64
- { name: 'openai/tiktoken', onAdd },
86
+ {
87
+ name: 'shep-ai/cli',
88
+ onAdd,
89
+ branch: 'main',
90
+ commitMessage: 'feat: add dark mode toggle',
91
+ committer: 'Jane Doe',
92
+ },
93
+ {
94
+ name: 'vercel/edge-runtime',
95
+ onAdd,
96
+ branch: 'feat/streaming',
97
+ commitMessage: 'fix: handle edge cases in streaming',
98
+ committer: 'John Smith',
99
+ behindCount: 3,
100
+ },
101
+ {
102
+ name: 'acme-corp/web-dashboard-v2',
103
+ onAdd,
104
+ branch: 'develop',
105
+ commitMessage: 'chore: update dependencies',
106
+ committer: 'Bot',
107
+ },
108
+ {
109
+ name: 'openai/tiktoken',
110
+ onAdd,
111
+ branch: 'main',
112
+ commitMessage: 'perf: optimize token encoding',
113
+ committer: 'Alice Chen',
114
+ },
65
115
  ];
66
116
  export const MultipleWithButton = {
67
117
  render: () => {
68
118
  const nodes = multipleReposWithButton.map((data, i) => ({
69
119
  id: `repo-${i}`,
70
120
  type: 'repositoryNode',
71
- position: { x: 0, y: i * 80 },
121
+ position: { x: 0, y: i * 140 },
72
122
  data,
73
123
  }));
74
- return (_jsx("div", { style: { width: 500, height: 500 }, children: _jsx(ReactFlowProvider, { children: _jsx(ReactFlow, { nodes: nodes, nodeTypes: nodeTypes, nodesDraggable: false, nodesConnectable: false, proOptions: { hideAttribution: true }, fitView: true }) }) }));
124
+ return (_jsx("div", { style: { width: 600, height: 750 }, children: _jsx(ReactFlowProvider, { children: _jsx(ReactFlow, { nodes: nodes, nodeTypes: nodeTypes, nodesDraggable: false, nodesConnectable: false, proOptions: { hideAttribution: true }, fitView: true }) }) }));
75
125
  },
76
126
  };
77
127
  export const WithPulseAdd = {
@@ -108,16 +158,29 @@ export const WithDeleteButton = {
108
158
  argTypes: {
109
159
  onDelete: { action: 'onDelete' },
110
160
  },
111
- render: (args) => _jsx(RepositoryNodeCanvas, { data: args, style: { width: 550, height: 250 } }),
161
+ render: (args) => _jsx(RepositoryNodeCanvas, { data: args, style: { width: 600, height: 300 } }),
112
162
  };
113
163
  const multipleReposWithActions = [
114
- { id: 'r1', name: 'shep-ai/cli', repositoryPath: '/home/user/shep-ai/cli', onAdd, onDelete },
164
+ {
165
+ id: 'r1',
166
+ name: 'shep-ai/cli',
167
+ repositoryPath: '/home/user/shep-ai/cli',
168
+ onAdd,
169
+ onDelete,
170
+ branch: 'main',
171
+ commitMessage: 'feat(web): enrich repository node with branch and commit info',
172
+ committer: 'Ariel Shadkhan',
173
+ },
115
174
  {
116
175
  id: 'r2',
117
176
  name: 'vercel/edge-runtime',
118
177
  repositoryPath: '/home/user/vercel/edge-runtime',
119
178
  onAdd,
120
179
  onDelete,
180
+ branch: 'feat/streaming-v2',
181
+ commitMessage: 'fix: handle edge cases in streaming response',
182
+ committer: 'John Smith',
183
+ behindCount: 5,
121
184
  },
122
185
  {
123
186
  id: 'r3',
@@ -125,6 +188,10 @@ const multipleReposWithActions = [
125
188
  repositoryPath: '/home/user/acme-corp/web-dashboard-v2',
126
189
  onAdd,
127
190
  onDelete,
191
+ branch: 'develop',
192
+ commitMessage: 'chore(deps): update all dependencies to latest',
193
+ committer: 'Renovate Bot',
194
+ behindCount: 23,
128
195
  },
129
196
  {
130
197
  id: 'r4',
@@ -132,6 +199,9 @@ const multipleReposWithActions = [
132
199
  repositoryPath: '/home/user/openai/tiktoken',
133
200
  onAdd,
134
201
  onDelete,
202
+ branch: 'main',
203
+ commitMessage: 'perf: optimize token encoding for large inputs',
204
+ committer: 'Alice Chen',
135
205
  },
136
206
  ];
137
207
  export const MultipleWithActions = {
@@ -139,9 +209,29 @@ export const MultipleWithActions = {
139
209
  const nodes = multipleReposWithActions.map((data, i) => ({
140
210
  id: `repo-${i}`,
141
211
  type: 'repositoryNode',
142
- position: { x: 0, y: i * 80 },
212
+ position: { x: 0, y: i * 140 },
143
213
  data,
144
214
  }));
145
- return (_jsx("div", { style: { width: 500, height: 500 }, children: _jsx(ReactFlowProvider, { children: _jsx(ReactFlow, { nodes: nodes, nodeTypes: nodeTypes, nodesDraggable: false, nodesConnectable: false, proOptions: { hideAttribution: true }, fitView: true }) }) }));
215
+ return (_jsx("div", { style: { width: 600, height: 800 }, children: _jsx(ReactFlowProvider, { children: _jsx(ReactFlow, { nodes: nodes, nodeTypes: nodeTypes, nodesDraggable: false, nodesConnectable: false, proOptions: { hideAttribution: true }, fitView: true }) }) }));
216
+ },
217
+ };
218
+ export const WithGitInfo = {
219
+ args: {
220
+ repositoryPath: '/home/user/shep-ai/cli',
221
+ branch: 'feat/repo-node-enrichment',
222
+ commitMessage: 'feat(web): enrich repository node with branch and commit info',
223
+ committer: 'Ariel Shadkhan',
224
+ behindCount: 0,
146
225
  },
226
+ render: (args) => _jsx(RepositoryNodeCanvas, { data: args }),
227
+ };
228
+ export const WithGitInfoBehind = {
229
+ args: {
230
+ repositoryPath: '/home/user/shep-ai/cli',
231
+ branch: 'feat/old-feature-branch',
232
+ commitMessage: 'fix: resolve merge conflict in auth module',
233
+ committer: 'Jane Doe',
234
+ behindCount: 7,
235
+ },
236
+ render: (args) => _jsx(RepositoryNodeCanvas, { data: args }),
147
237
  };
@@ -1 +1 @@
1
- {"version":3,"file":"control-center-empty-state.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/control-center-empty-state.tsx"],"names":[],"mappings":"AAqBA,MAAM,WAAW,4BAA4B;IAC3C,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAID,wBAAgB,uBAAuB,CAAC,EACtC,kBAAkB,EAClB,SAAS,GACV,EAAE,4BAA4B,kDA+I9B"}
1
+ {"version":3,"file":"control-center-empty-state.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/control-center-empty-state.tsx"],"names":[],"mappings":"AAoBA,MAAM,WAAW,4BAA4B;IAC3C,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAID,wBAAgB,uBAAuB,CAAC,EACtC,kBAAkB,EAClB,SAAS,GACV,EAAE,4BAA4B,kDA8J9B"}
@@ -1,7 +1,7 @@
1
1
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState, useCallback, useEffect } from 'react';
4
- import { Github, Plus, TerminalSquare, Copy, Check, Loader2, CheckCircle2, AlertCircle, Terminal, } from 'lucide-react';
4
+ import { FolderOpen, Copy, Check, Loader2, CheckCircle2, AlertCircle, Terminal, ChevronDown, } from 'lucide-react';
5
5
  import { cn } from '../../../lib/utils.js';
6
6
  import { pickFolder } from '../../common/add-repository-button/pick-folder.js';
7
7
  import { isAgentSetupComplete } from '../../../app/actions/agent-setup-flag.js';
@@ -13,12 +13,12 @@ export function ControlCenterEmptyState({ onRepositorySelect, className, }) {
13
13
  const [loading, setLoading] = useState(false);
14
14
  const [agentReady, setAgentReady] = useState(null);
15
15
  const [authStatus, setAuthStatus] = useState(null);
16
+ const [cliExpanded, setCliExpanded] = useState(false);
16
17
  useEffect(() => {
17
18
  isAgentSetupComplete().then((done) => {
18
19
  setAgentReady(done);
19
20
  });
20
21
  }, []);
21
- // Background auth check — runs after agent setup is complete
22
22
  useEffect(() => {
23
23
  if (!agentReady)
24
24
  return;
@@ -52,28 +52,28 @@ export function ControlCenterEmptyState({ onRepositorySelect, className, }) {
52
52
  }, []);
53
53
  if (agentReady === null)
54
54
  return null;
55
- return (_jsx("div", { "data-testid": "control-center-empty-state", className: cn('flex flex-col items-center justify-center', className), children: _jsx("div", { className: "animate-in fade-in slide-in-from-bottom-2 w-96 rounded-2xl border border-white/30 bg-white/50 px-8 py-8 shadow-lg backdrop-blur-xl duration-300 dark:border-white/10 dark:bg-white/5", children: _jsxs("div", { className: "flex flex-col items-center gap-4", children: [_jsxs("div", { className: "mb-2 text-center", children: [_jsx("h1", { className: "text-xl font-bold tracking-tight", children: "Features" }), _jsx("p", { className: "text-muted-foreground text-sm font-light", children: "Control Center" })] }), !agentReady ? (
56
- /* Step 1: Agent setup */
57
- _jsx(WelcomeAgentSetup, { onComplete: handleAgentSetupComplete, className: "w-full" })) : (
58
- /* Step 2: Add repository (shown immediately after agent is configured) */
59
- _jsxs(_Fragment, { children: [_jsx(AgentAuthBanner, { status: authStatus, onRetry: handleRetryAuth }), _jsxs("div", { className: "text-muted-foreground flex items-center gap-2", children: [_jsx(Github, { className: "h-3.5 w-3.5" }), _jsx("span", { className: "text-[10px] font-semibold tracking-widest uppercase", children: "Repositories" })] }), _jsxs("button", { type: "button", "data-testid": "empty-state-add-repository", onClick: handlePickerClick, disabled: loading, className: "border-border hover:border-foreground/40 flex w-full cursor-pointer items-center gap-2.5 rounded-md border border-dashed px-4 py-2.5 transition-colors disabled:cursor-wait disabled:opacity-50", children: [loading ? (_jsx(Loader2, { className: "text-muted-foreground h-4 w-4 animate-spin" })) : (_jsx(Plus, { className: "text-muted-foreground h-4 w-4" })), _jsx("span", { className: "text-xs font-medium", children: loading ? 'Opening…' : 'Add Repository' })] }), _jsxs("div", { className: "text-muted-foreground flex items-center gap-2", children: [_jsx("div", { className: "bg-border h-px w-10" }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx(TerminalSquare, { className: "h-3 w-3" }), _jsx("span", { className: "text-[10px] font-semibold tracking-widest uppercase", children: "Or via CLI" })] }), _jsx("div", { className: "bg-border h-px w-10" })] }), _jsxs("div", { "data-testid": "cli-code-block", className: "relative w-full rounded-md bg-zinc-900 px-3 py-2 font-mono text-[11px] text-zinc-300", children: [_jsx("button", { type: "button", "data-testid": "cli-code-block-copy", onClick: handleCopy, className: "absolute top-2 right-2 cursor-pointer rounded p-1 text-zinc-500 transition-colors hover:bg-zinc-800 hover:text-zinc-300", "aria-label": "Copy commands", children: copied ? _jsx(Check, { className: "h-3 w-3" }) : _jsx(Copy, { className: "h-3 w-3" }) }), _jsx("div", { className: "space-y-0.5", children: commands.map((cmd) => (_jsxs("div", { className: "whitespace-nowrap", children: [_jsx("span", { className: "text-zinc-500 select-none", children: "$ " }), _jsx("span", { children: cmd })] }, cmd))) })] }), _jsx("p", { className: "text-muted-foreground text-[10px]", children: "Feature will appear on canvas once created" })] }))] }) }) }));
55
+ return (_jsxs("div", { "data-testid": "control-center-empty-state", className: cn('relative flex h-full w-full flex-col items-center justify-center px-8', className), children: [!agentReady ? (
56
+ /* Agent setup wizard — owns its own hero */
57
+ _jsx(WelcomeAgentSetup, { onComplete: handleAgentSetupComplete })) : (
58
+ /* Repository step fade in to match wizard transitions */
59
+ _jsxs("div", { className: "animate-in fade-in flex w-full max-w-md flex-col items-center duration-300", children: [_jsx("h1", { className: "text-foreground/90 text-center text-5xl font-extralight tracking-tight", children: "Add a project" }), _jsxs("p", { className: "text-muted-foreground mt-3 text-center text-lg leading-relaxed font-light", children: ["Add your project folder to unlock feature creation.", _jsx("br", {}), "Describe what you need \u2014 Shep handles the rest."] }), _jsx("div", { className: "mt-8", children: _jsx(AgentAuthBanner, { status: authStatus, onRetry: handleRetryAuth }) }), _jsxs("button", { type: "button", "data-testid": "empty-state-add-repository", onClick: handlePickerClick, disabled: loading, className: "bg-foreground text-background hover:bg-foreground/90 mt-10 flex w-full cursor-pointer items-center justify-center gap-2.5 rounded-xl px-6 py-4 text-base font-medium shadow-lg transition-all duration-200 hover:shadow-xl active:scale-[0.98] disabled:cursor-wait disabled:opacity-50", children: [loading ? (_jsx(Loader2, { className: "h-5 w-5 animate-spin" })) : (_jsx(FolderOpen, { className: "h-5 w-5" })), loading ? 'Opening…' : 'Choose a Folder'] }), _jsx("p", { className: "text-muted-foreground/60 mt-3 text-center text-sm", children: "Any folder works \u2014 git will be initialized automatically if needed." })] })), agentReady ? (_jsxs("div", { className: "absolute bottom-8 flex flex-col items-center", style: {
60
+ animationDelay: '400ms',
61
+ animationDuration: '600ms',
62
+ animationFillMode: 'both',
63
+ }, children: [_jsxs("button", { type: "button", onClick: () => setCliExpanded(!cliExpanded), className: "text-muted-foreground hover:text-foreground flex cursor-pointer items-center gap-1.5 transition-colors duration-200", children: [_jsx(Terminal, { className: "h-3.5 w-3.5" }), _jsx("span", { className: "text-sm", children: "or use the CLI" }), _jsx(ChevronDown, { className: cn('h-3.5 w-3.5 transition-transform duration-200', cliExpanded ? '' : 'rotate-180') })] }), cliExpanded ? (_jsx("div", { className: "animate-in fade-in slide-in-from-top-1 mt-3 w-80 duration-200", children: _jsxs("div", { "data-testid": "cli-code-block", className: "relative rounded-xl bg-zinc-900 px-5 py-4 font-mono text-[13px] leading-relaxed text-zinc-400", children: [_jsx("button", { type: "button", "data-testid": "cli-code-block-copy", onClick: handleCopy, className: "absolute top-3 right-3 cursor-pointer rounded-md p-1.5 text-zinc-600 transition-colors hover:bg-zinc-800 hover:text-zinc-300", "aria-label": "Copy commands", children: copied ? (_jsx(Check, { className: "h-3.5 w-3.5 text-emerald-400" })) : (_jsx(Copy, { className: "h-3.5 w-3.5" })) }), _jsx("div", { className: "space-y-1", children: commands.map((cmd) => (_jsxs("div", { className: "whitespace-nowrap", children: [_jsx("span", { className: "text-zinc-600 select-none", children: "$ " }), _jsx("span", { className: "text-zinc-300", children: cmd })] }, cmd))) })] }) })) : null] })) : null] }));
60
64
  }
61
- /** Compact auth status bannershows checking/success/error states */
65
+ /** Inline status pillminimal, no background box */
62
66
  function AgentAuthBanner({ status, onRetry, }) {
63
- // Still checking — show subtle spinner
64
67
  if (!status) {
65
- return (_jsxs("div", { className: "bg-muted/50 flex w-full items-center gap-2 rounded-md px-3 py-1.5", children: [_jsx(Loader2, { className: "text-muted-foreground h-3 w-3 animate-spin" }), _jsx("span", { className: "text-muted-foreground text-[10px]", children: "Checking agent setup\u2026" })] }));
68
+ return (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Loader2, { className: "text-muted-foreground/50 h-3.5 w-3.5 animate-spin" }), _jsx("span", { className: "text-muted-foreground/50 text-sm", children: "Checking setup\u2026" })] }));
66
69
  }
67
- // All good — compact success, auto-fades
68
70
  if (status.installed && status.authenticated) {
69
- return (_jsxs("div", { className: "animate-in fade-in flex w-full items-center gap-2 rounded-md bg-emerald-500/10 px-3 py-1.5", children: [_jsx(CheckCircle2, { className: "h-3 w-3 text-emerald-600 dark:text-emerald-400" }), _jsxs("span", { className: "text-[10px] text-emerald-700 dark:text-emerald-300", children: [status.label, " ready"] })] }));
71
+ return (_jsxs("div", { className: "animate-in fade-in flex items-center gap-2", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-emerald-500" }), _jsxs("span", { className: "text-sm text-emerald-600 dark:text-emerald-400", children: [status.label, " ready"] })] }));
70
72
  }
71
- // Not installed
72
73
  if (!status.installed) {
73
- return (_jsxs("div", { className: "flex w-full flex-col gap-2 rounded-md bg-amber-500/10 px-3 py-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(AlertCircle, { className: "h-3 w-3 text-amber-600 dark:text-amber-400" }), _jsxs("span", { className: "text-[10px] font-medium text-amber-700 dark:text-amber-300", children: [status.label, " not installed"] })] }), status.binaryName ? (_jsxs("div", { className: "rounded bg-zinc-900 px-2 py-1 font-mono text-[10px] text-zinc-300", children: [_jsx("span", { className: "text-zinc-500 select-none", children: "$ " }), "npm install -g ", status.binaryName] })) : null, _jsx("button", { type: "button", onClick: onRetry, className: "self-start text-[10px] font-medium text-amber-700 underline underline-offset-2 hover:text-amber-900 dark:text-amber-300 dark:hover:text-amber-100", children: "Re-check" })] }));
74
+ return (_jsxs("div", { className: "flex flex-col items-center gap-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(AlertCircle, { className: "h-4 w-4 text-amber-500" }), _jsxs("span", { className: "text-sm font-medium text-amber-600 dark:text-amber-400", children: [status.label, " not installed"] })] }), status.binaryName ? (_jsxs("code", { className: "rounded-lg bg-zinc-100 px-3 py-1.5 text-xs text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400", children: ["npm install -g ", status.binaryName] })) : null, _jsx("button", { type: "button", onClick: onRetry, className: "text-xs text-amber-600 underline underline-offset-2 hover:text-amber-800 dark:text-amber-400", children: "Re-check" })] }));
74
75
  }
75
- // Installed but not authenticated
76
- return (_jsxs("div", { className: "flex w-full flex-col gap-2 rounded-md bg-amber-500/10 px-3 py-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(AlertCircle, { className: "h-3 w-3 text-amber-600 dark:text-amber-400" }), _jsxs("span", { className: "text-[10px] font-medium text-amber-700 dark:text-amber-300", children: [status.label, " needs authentication"] })] }), status.authCommand ? (_jsxs("div", { className: "rounded bg-zinc-900 px-2 py-1 font-mono text-[10px] text-zinc-300", children: [_jsx("span", { className: "text-zinc-500 select-none", children: "$ " }), status.authCommand] })) : null, _jsxs("div", { className: "flex items-center gap-2", children: [status.binaryName ? (_jsxs("button", { type: "button", "data-testid": "auth-banner-open-terminal", onClick: async () => {
76
+ return (_jsxs("div", { className: "flex flex-col items-center gap-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(AlertCircle, { className: "h-4 w-4 text-amber-500" }), _jsxs("span", { className: "text-sm font-medium text-amber-600 dark:text-amber-400", children: [status.label, " needs authentication"] })] }), status.authCommand ? (_jsx("code", { className: "rounded-lg bg-zinc-100 px-3 py-1.5 text-xs text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400", children: status.authCommand })) : null, _jsxs("div", { className: "flex items-center gap-3", children: [status.binaryName ? (_jsxs("button", { type: "button", "data-testid": "auth-banner-open-terminal", onClick: async () => {
77
77
  try {
78
78
  const toolId = status.agentType === 'claude-code' ? 'claude-code' : status.agentType;
79
79
  await fetch(`/api/tools/${toolId}/launch`, { method: 'POST' });
@@ -81,5 +81,5 @@ function AgentAuthBanner({ status, onRetry, }) {
81
81
  catch {
82
82
  // best effort
83
83
  }
84
- }, className: "flex items-center gap-1 rounded border border-amber-600/30 px-2 py-0.5 text-[10px] font-medium text-amber-700 transition-colors hover:bg-amber-500/10 dark:text-amber-300", children: [_jsx(Terminal, { className: "h-2.5 w-2.5" }), "Open ", status.label] })) : null, _jsx("button", { type: "button", onClick: onRetry, className: "text-[10px] font-medium text-amber-700 underline underline-offset-2 hover:text-amber-900 dark:text-amber-300 dark:hover:text-amber-100", children: "Re-check" })] })] }));
84
+ }, className: "flex items-center gap-1.5 text-xs font-medium text-amber-600 underline underline-offset-2 hover:text-amber-800 dark:text-amber-400", children: [_jsx(Terminal, { className: "h-3 w-3" }), "Open ", status.label] })) : null, _jsx("button", { type: "button", onClick: onRetry, className: "text-xs text-amber-600 underline underline-offset-2 hover:text-amber-800 dark:text-amber-400", children: "Re-check" })] })] }));
85
85
  }
@@ -1 +1 @@
1
- {"version":3,"file":"control-center-inner.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/control-center-inner.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,IAAI,EAAY,MAAM,eAAe,CAAC;AAGpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAuB5E,UAAU,uBAAuB;IAC/B,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B,YAAY,EAAE,IAAI,EAAE,CAAC;CACtB;AAED,wBAAgB,kBAAkB,CAAC,EAAE,YAAY,EAAE,YAAY,EAAE,EAAE,uBAAuB,2CA2UzF"}
1
+ {"version":3,"file":"control-center-inner.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/control-center-inner.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,IAAI,EAAY,MAAM,eAAe,CAAC;AAGpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAuB5E,UAAU,uBAAuB;IAC/B,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B,YAAY,EAAE,IAAI,EAAE,CAAC;CACtB;AAED,wBAAgB,kBAAkB,CAAC,EAAE,YAAY,EAAE,YAAY,EAAE,EAAE,uBAAuB,2CAkWzF"}
@@ -27,8 +27,8 @@ export function ControlCenterInner({ initialNodes, initialEdges }) {
27
27
  const drawerTimerRef = useRef(null);
28
28
  const { defaultViewport, onMoveEnd: handleViewportChange, resetViewport, } = useViewportPersistence();
29
29
  const { nodes, edges, onNodesChange, handleConnect, handleAddRepository, handleDeleteFeature, handleRetryFeature, handleStartFeature, handleStopFeature, handleDeleteRepository, createFeatureNode, setCallbacks, } = useControlCenterState(initialNodes, initialEdges);
30
- // Publish sidebar features to context whenever feature node data changes
31
- const { setFeatures: setSidebarFeatures } = useSidebarFeaturesContext();
30
+ // Publish sidebar features + repo state to context
31
+ const { setFeatures: setSidebarFeatures, setHasRepositories: setSidebarHasRepos } = useSidebarFeaturesContext();
32
32
  const featureNodes = useMemo(() => nodes.filter((n) => n.type === 'featureNode'), [nodes]);
33
33
  const sidebarKey = useMemo(() => {
34
34
  return featureNodes
@@ -119,21 +119,29 @@ export function ControlCenterInner({ initialNodes, initialEdges }) {
119
119
  guardedNavigate(() => router.push('/'));
120
120
  }
121
121
  }, [router, pathname, guardedNavigate]);
122
+ // Shared: after adding first repo, center canvas and open create drawer
123
+ const focusAndOpenDrawer = useCallback((repoPath) => {
124
+ // Wait for next render so the repo node exists in the DOM
125
+ setTimeout(() => {
126
+ fitView(AUTO_FOCUS_OPTIONS);
127
+ // Open the create-feature drawer after the fitView animation completes
128
+ drawerTimerRef.current = setTimeout(() => {
129
+ guardedNavigate(() => router.push(`/create?repo=${encodeURIComponent(repoPath)}`));
130
+ }, AUTO_FOCUS_DRAWER_DELAY_MS);
131
+ }, 0);
132
+ }, [fitView, guardedNavigate, router]);
133
+ // Wrapper: add repo + auto-focus if canvas was empty
134
+ const addRepoAndFocus = useCallback((path) => {
135
+ const { wasEmpty, repoPath } = handleAddRepository(path);
136
+ if (wasEmpty) {
137
+ focusAndOpenDrawer(repoPath);
138
+ }
139
+ }, [handleAddRepository, focusAndOpenDrawer]);
122
140
  // Listen for global "add repository" events from the top bar button
123
141
  useEffect(() => {
124
142
  const handler = (e) => {
125
143
  const path = e.detail.path;
126
- const { wasEmpty, repoPath } = handleAddRepository(path);
127
- if (wasEmpty) {
128
- // Wait for next render so the repo node exists in the DOM, then auto-focus
129
- setTimeout(() => {
130
- fitView(AUTO_FOCUS_OPTIONS);
131
- // Open the create-feature drawer after the fitView animation completes
132
- drawerTimerRef.current = setTimeout(() => {
133
- guardedNavigate(() => router.push(`/create?repo=${encodeURIComponent(repoPath)}`));
134
- }, AUTO_FOCUS_DRAWER_DELAY_MS);
135
- }, 0);
136
- }
144
+ addRepoAndFocus(path);
137
145
  };
138
146
  window.addEventListener('shep:add-repository', handler);
139
147
  return () => {
@@ -142,7 +150,7 @@ export function ControlCenterInner({ initialNodes, initialEdges }) {
142
150
  clearTimeout(drawerTimerRef.current);
143
151
  }
144
152
  };
145
- }, [handleAddRepository, fitView, guardedNavigate, router]);
153
+ }, [addRepoAndFocus]);
146
154
  // Listen for create events from the create drawer (with real feature ID from server)
147
155
  useEffect(() => {
148
156
  const handler = (e) => {
@@ -212,6 +220,10 @@ export function ControlCenterInner({ initialNodes, initialEdges }) {
212
220
  handleViewportChange(viewport);
213
221
  }, [handleViewportChange]);
214
222
  const hasRepositories = nodes.some((n) => n.type === 'repositoryNode');
223
+ // Publish repo state to sidebar context so AppShell can hide FAB during onboarding
224
+ useEffect(() => {
225
+ setSidebarHasRepos(hasRepositories);
226
+ }, [hasRepositories, setSidebarHasRepos]);
215
227
  // Debounced latch: prevent empty-state flicker during brief reconcile gaps
216
228
  // (e.g. stale poll momentarily drops repos), but allow the empty state to
217
229
  // return after a real delete once repos stay gone past the debounce window.
@@ -227,7 +239,9 @@ export function ControlCenterInner({ initialNodes, initialEdges }) {
227
239
  setShowCanvas(true);
228
240
  }
229
241
  else if (showCanvas) {
230
- // Repos gone — wait before showing empty state (debounce stale polls)
242
+ // Repos gone — clear saved viewport so next add starts centered,
243
+ // then wait before showing empty state (debounce stale polls)
244
+ resetViewport();
231
245
  latchTimerRef.current = setTimeout(() => {
232
246
  setShowCanvas(false);
233
247
  latchTimerRef.current = null;
@@ -237,7 +251,7 @@ export function ControlCenterInner({ initialNodes, initialEdges }) {
237
251
  if (latchTimerRef.current)
238
252
  clearTimeout(latchTimerRef.current);
239
253
  };
240
- }, [hasRepositories, showCanvas]);
254
+ }, [hasRepositories, showCanvas, resetViewport]);
241
255
  // Pulse the "+" button when there's a single repo with no features and the
242
256
  // create-feature drawer is not open — draws attention to the next action.
243
257
  const isCreateDrawerOpen = pathname.startsWith('/create');
@@ -249,5 +263,5 @@ export function ControlCenterInner({ initialNodes, initialEdges }) {
249
263
  return nodes;
250
264
  return nodes.map((n) => n.type === 'repositoryNode' ? { ...n, data: { ...n.data, pulseAdd: true } } : n);
251
265
  }, [nodes, isCreateDrawerOpen]);
252
- return (_jsx(FeaturesCanvas, { nodes: showCanvas ? displayNodes : [], edges: showCanvas ? edges : [], selectedFeatureId: selectedFeatureId, defaultViewport: defaultViewport, onNodesChange: onNodesChange, onConnect: handleConnect, onAddFeature: handleAddFeature, onNodeClick: handleNodeClick, onPaneClick: handleClearDrawers, onMoveEnd: handleMoveEnd, onResetViewport: resetViewport, emptyState: _jsx(ControlCenterEmptyState, { onRepositorySelect: handleAddRepository }) }));
266
+ return (_jsx(FeaturesCanvas, { nodes: showCanvas ? displayNodes : [], edges: showCanvas ? edges : [], selectedFeatureId: selectedFeatureId, defaultViewport: defaultViewport, onNodesChange: onNodesChange, onConnect: handleConnect, onAddFeature: handleAddFeature, onNodeClick: handleNodeClick, onPaneClick: handleClearDrawers, onMoveEnd: handleMoveEnd, onResetViewport: resetViewport, emptyState: _jsx(ControlCenterEmptyState, { onRepositorySelect: addRepoAndFocus }) }));
253
267
  }
@@ -1 +1 @@
1
- {"version":3,"file":"welcome-agent-setup.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/welcome-agent-setup.tsx"],"names":[],"mappings":"AAWA,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AASD,wBAAgB,iBAAiB,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE,sBAAsB,2CAuNlF"}
1
+ {"version":3,"file":"welcome-agent-setup.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/welcome-agent-setup.tsx"],"names":[],"mappings":"AAWA,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAMD,wBAAgB,iBAAiB,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE,sBAAsB,2CA4NlF"}