@shepai/cli 1.72.6 → 1.72.7

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 (244) hide show
  1. package/dist/packages/core/src/application/use-cases/features/delete-feature.use-case.d.ts +4 -0
  2. package/dist/packages/core/src/application/use-cases/features/delete-feature.use-case.d.ts.map +1 -1
  3. package/dist/packages/core/src/application/use-cases/features/delete-feature.use-case.js +20 -12
  4. package/dist/packages/core/src/application/use-cases/repositories/delete-repository.use-case.d.ts +8 -3
  5. package/dist/packages/core/src/application/use-cases/repositories/delete-repository.use-case.d.ts.map +1 -1
  6. package/dist/packages/core/src/application/use-cases/repositories/delete-repository.use-case.js +17 -4
  7. package/dist/packages/core/src/infrastructure/repositories/sqlite-feature.repository.js +1 -1
  8. package/dist/src/presentation/web/app/(dashboard)/get-graph-data.d.ts.map +1 -1
  9. package/dist/src/presentation/web/app/(dashboard)/get-graph-data.js +2 -6
  10. package/dist/src/presentation/web/app/build-graph-nodes.js +11 -1
  11. package/dist/src/presentation/web/components/common/base-drawer/base-drawer.d.ts +3 -1
  12. package/dist/src/presentation/web/components/common/base-drawer/base-drawer.d.ts.map +1 -1
  13. package/dist/src/presentation/web/components/common/base-drawer/base-drawer.js +8 -4
  14. package/dist/src/presentation/web/components/common/control-center-drawer/create-drawer-client.d.ts.map +1 -1
  15. package/dist/src/presentation/web/components/common/control-center-drawer/create-drawer-client.js +12 -3
  16. package/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.d.ts.map +1 -1
  17. package/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.js +6 -2
  18. package/dist/src/presentation/web/components/common/drawer-action-bar/drawer-action-bar.d.ts.map +1 -1
  19. package/dist/src/presentation/web/components/common/drawer-action-bar/drawer-action-bar.js +1 -1
  20. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.d.ts.map +1 -1
  21. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.js +1 -1
  22. package/dist/src/presentation/web/components/common/feature-node/feature-node.d.ts.map +1 -1
  23. package/dist/src/presentation/web/components/common/feature-node/feature-node.js +7 -2
  24. package/dist/src/presentation/web/components/common/repository-node/repository-node.d.ts.map +1 -1
  25. package/dist/src/presentation/web/components/common/repository-node/repository-node.js +6 -2
  26. package/dist/src/presentation/web/components/features/control-center/control-center-inner.d.ts.map +1 -1
  27. package/dist/src/presentation/web/components/features/control-center/control-center-inner.js +14 -2
  28. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts +3 -5
  29. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts.map +1 -1
  30. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.js +131 -196
  31. package/dist/src/presentation/web/components/ui/alert-dialog.d.ts +1 -1
  32. package/dist/src/presentation/web/components/ui/alert-dialog.d.ts.map +1 -1
  33. package/dist/src/presentation/web/components/ui/alert-dialog.js +7 -2
  34. package/dist/src/presentation/web/components/ui/dialog.d.ts.map +1 -1
  35. package/dist/src/presentation/web/components/ui/dialog.js +6 -1
  36. package/dist/src/presentation/web/lib/layout-with-dagre.d.ts +2 -0
  37. package/dist/src/presentation/web/lib/layout-with-dagre.d.ts.map +1 -1
  38. package/dist/src/presentation/web/lib/layout-with-dagre.js +86 -4
  39. package/dist/tsconfig.build.tsbuildinfo +1 -1
  40. package/package.json +1 -1
  41. package/web/.next/BUILD_ID +1 -1
  42. package/web/.next/build-manifest.json +2 -2
  43. package/web/.next/cache/.previewinfo +1 -1
  44. package/web/.next/cache/.rscinfo +1 -1
  45. package/web/.next/cache/.tsbuildinfo +1 -1
  46. package/web/.next/cache/config.json +3 -3
  47. package/web/.next/fallback-build-manifest.json +2 -2
  48. package/web/.next/prerender-manifest.json +3 -3
  49. package/web/.next/required-server-files.js +1 -1
  50. package/web/.next/required-server-files.json +1 -1
  51. package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +14 -14
  52. package/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
  53. package/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
  54. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +17 -17
  55. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
  56. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
  57. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +12 -12
  58. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
  59. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  60. package/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +14 -14
  61. package/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
  62. package/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
  63. package/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +17 -17
  64. package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
  65. package/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
  66. package/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +12 -12
  67. package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
  68. package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
  69. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +12 -12
  70. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
  71. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  72. package/web/.next/server/app/_global-error.html +2 -2
  73. package/web/.next/server/app/_global-error.rsc +1 -1
  74. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  75. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  76. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  77. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  78. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  79. package/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  80. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  81. package/web/.next/server/app/skills/page/server-reference-manifest.json +6 -6
  82. package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  83. package/web/.next/server/app/tools/page/server-reference-manifest.json +1 -1
  84. package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  85. package/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
  86. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  87. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
  88. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js.map +1 -1
  89. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_2b7834f6.js +1 -1
  90. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_2b7834f6.js.map +1 -1
  91. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_6923c575.js +1 -1
  92. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_6923c575.js.map +1 -1
  93. package/web/.next/server/chunks/ssr/[root-of-the-server]__07385073._.js +2 -2
  94. package/web/.next/server/chunks/ssr/[root-of-the-server]__07385073._.js.map +1 -1
  95. package/web/.next/server/chunks/ssr/[root-of-the-server]__08ba9bd3._.js +1 -1
  96. package/web/.next/server/chunks/ssr/[root-of-the-server]__08ba9bd3._.js.map +1 -1
  97. package/web/.next/server/chunks/ssr/[root-of-the-server]__249c74f6._.js +1 -1
  98. package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +1 -1
  99. package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js.map +1 -1
  100. package/web/.next/server/chunks/ssr/[root-of-the-server]__6bb00d90._.js +1 -1
  101. package/web/.next/server/chunks/ssr/[root-of-the-server]__6bb00d90._.js.map +1 -1
  102. package/web/.next/server/chunks/ssr/[root-of-the-server]__6db85a66._.js +2 -2
  103. package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js +1 -1
  104. package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js.map +1 -1
  105. package/web/.next/server/chunks/ssr/[root-of-the-server]__8fcb9132._.js +2 -2
  106. package/web/.next/server/chunks/ssr/_00e6c698._.js +1 -1
  107. package/web/.next/server/chunks/ssr/_00e6c698._.js.map +1 -1
  108. package/web/.next/server/chunks/ssr/_07c2a32a._.js +1 -1
  109. package/web/.next/server/chunks/ssr/_07c2a32a._.js.map +1 -1
  110. package/web/.next/server/chunks/ssr/_34a58db8._.js +1 -1
  111. package/web/.next/server/chunks/ssr/_34a58db8._.js.map +1 -1
  112. package/web/.next/server/chunks/ssr/_37259405._.js +1 -1
  113. package/web/.next/server/chunks/ssr/_37259405._.js.map +1 -1
  114. package/web/.next/server/chunks/ssr/{_85d0351b._.js → _63bfa7dd._.js} +2 -2
  115. package/web/.next/server/chunks/ssr/_63bfa7dd._.js.map +1 -0
  116. package/web/.next/{standalone/src/presentation/web/.next/server/chunks/ssr/_e50484ec._.js → server/chunks/ssr/_7215bb0f._.js} +2 -2
  117. package/web/.next/server/chunks/ssr/{_e50484ec._.js.map → _7215bb0f._.js.map} +1 -1
  118. package/web/.next/server/chunks/ssr/_cb5b3951._.js +1 -1
  119. package/web/.next/server/chunks/ssr/_cb5b3951._.js.map +1 -1
  120. package/web/.next/server/chunks/ssr/_fc3a6895._.js +1 -1
  121. package/web/.next/server/chunks/ssr/_fc3a6895._.js.map +1 -1
  122. package/web/.next/server/chunks/ssr/src_presentation_web_1a32dbcb._.js +2 -2
  123. package/web/.next/server/chunks/ssr/src_presentation_web_1a32dbcb._.js.map +1 -1
  124. package/web/.next/server/chunks/ssr/src_presentation_web_398553b0._.js +1 -1
  125. package/web/.next/server/chunks/ssr/src_presentation_web_398553b0._.js.map +1 -1
  126. package/web/.next/server/chunks/ssr/src_presentation_web_5c87e8e4._.js +1 -1
  127. package/web/.next/server/chunks/ssr/src_presentation_web_5c87e8e4._.js.map +1 -1
  128. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_f4952172.js +1 -1
  129. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_f4952172.js.map +1 -1
  130. package/web/.next/server/chunks/ssr/src_presentation_web_c1bc6240._.js +2 -2
  131. package/web/.next/server/chunks/ssr/src_presentation_web_c1bc6240._.js.map +1 -1
  132. package/web/.next/server/chunks/ssr/src_presentation_web_ca99d62d._.js +1 -1
  133. package/web/.next/server/chunks/ssr/src_presentation_web_ca99d62d._.js.map +1 -1
  134. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
  135. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js.map +1 -1
  136. package/web/.next/server/pages/500.html +2 -2
  137. package/web/.next/server/server-reference-manifest.js +1 -1
  138. package/web/.next/server/server-reference-manifest.json +20 -20
  139. package/web/.next/standalone/src/presentation/web/.next/BUILD_ID +1 -1
  140. package/web/.next/standalone/src/presentation/web/.next/build-manifest.json +2 -2
  141. package/web/.next/standalone/src/presentation/web/.next/prerender-manifest.json +3 -3
  142. package/web/.next/standalone/src/presentation/web/.next/required-server-files.json +1 -1
  143. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +14 -14
  144. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
  145. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
  146. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +17 -17
  147. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
  148. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
  149. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +12 -12
  150. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
  151. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  152. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +14 -14
  153. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
  154. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
  155. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +17 -17
  156. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
  157. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
  158. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +12 -12
  159. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
  160. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
  161. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +12 -12
  162. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
  163. package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  164. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.html +2 -2
  165. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.rsc +1 -1
  166. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  167. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  168. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  169. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  170. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  171. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  172. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  173. package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page/server-reference-manifest.json +6 -6
  174. package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  175. package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page/server-reference-manifest.json +1 -1
  176. package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  177. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
  178. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  179. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
  180. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_2b7834f6.js +1 -1
  181. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_6923c575.js +1 -1
  182. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__07385073._.js +2 -2
  183. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__08ba9bd3._.js +1 -1
  184. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__249c74f6._.js +1 -1
  185. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +1 -1
  186. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__6bb00d90._.js +1 -1
  187. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__6db85a66._.js +2 -2
  188. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js +1 -1
  189. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__8fcb9132._.js +2 -2
  190. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_00e6c698._.js +1 -1
  191. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_07c2a32a._.js +1 -1
  192. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_34a58db8._.js +1 -1
  193. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_37259405._.js +1 -1
  194. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/{_85d0351b._.js → _63bfa7dd._.js} +2 -2
  195. package/web/.next/{server/chunks/ssr/_e50484ec._.js → standalone/src/presentation/web/.next/server/chunks/ssr/_7215bb0f._.js} +2 -2
  196. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_cb5b3951._.js +1 -1
  197. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_fc3a6895._.js +1 -1
  198. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_1a32dbcb._.js +2 -2
  199. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_398553b0._.js +1 -1
  200. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_5c87e8e4._.js +1 -1
  201. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_f4952172.js +1 -1
  202. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_c1bc6240._.js +2 -2
  203. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_ca99d62d._.js +1 -1
  204. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
  205. package/web/.next/standalone/src/presentation/web/.next/server/pages/500.html +2 -2
  206. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.js +1 -1
  207. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.json +20 -20
  208. package/web/.next/standalone/src/presentation/web/app/(dashboard)/get-graph-data.ts +2 -6
  209. package/web/.next/standalone/src/presentation/web/app/build-graph-nodes.ts +14 -1
  210. package/web/.next/standalone/src/presentation/web/components/common/base-drawer/base-drawer.tsx +10 -8
  211. package/web/.next/standalone/src/presentation/web/components/common/control-center-drawer/create-drawer-client.tsx +13 -3
  212. package/web/.next/standalone/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.tsx +6 -2
  213. package/web/.next/standalone/src/presentation/web/components/common/drawer-action-bar/drawer-action-bar.tsx +2 -0
  214. package/web/.next/standalone/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.tsx +8 -5
  215. package/web/.next/standalone/src/presentation/web/components/common/feature-node/feature-node.tsx +30 -24
  216. package/web/.next/standalone/src/presentation/web/components/common/repository-node/repository-node.tsx +33 -25
  217. package/web/.next/standalone/src/presentation/web/components/features/control-center/control-center-inner.tsx +25 -3
  218. package/web/.next/standalone/src/presentation/web/components/features/control-center/use-control-center-state.ts +184 -230
  219. package/web/.next/standalone/src/presentation/web/components/ui/alert-dialog.tsx +7 -0
  220. package/web/.next/standalone/src/presentation/web/components/ui/dialog.tsx +7 -1
  221. package/web/.next/standalone/src/presentation/web/lib/layout-with-dagre.ts +93 -4
  222. package/web/.next/standalone/src/presentation/web/server.js +1 -1
  223. package/web/.next/static/chunks/0c54314c8c9c9739.js +7 -0
  224. package/web/.next/static/chunks/{5f5c607ffae8b918.js → 304c8593d0a6525d.js} +1 -1
  225. package/web/.next/static/chunks/4558d1663c6f4d2c.js +1 -0
  226. package/web/.next/static/chunks/{32957ff277daec59.js → 4df07e765b233622.js} +1 -1
  227. package/web/.next/static/chunks/{fc07834e422b1caa.js → 585ae19c520e8b72.js} +1 -1
  228. package/web/.next/static/chunks/{ecb2130572a7fbe7.js → 6e14d8bd5c005e6e.js} +1 -1
  229. package/web/.next/static/chunks/{412a3ac70c529c5b.js → 8a1eb93510e54db0.js} +1 -1
  230. package/web/.next/static/chunks/{550bf3cb8220cbf0.js → b143c7d78a6888e9.js} +1 -1
  231. package/web/.next/static/chunks/{8b36e07129a1471f.js → b45344579a43cbb8.js} +1 -1
  232. package/web/.next/static/chunks/dec13bccb136ef7c.css +2 -0
  233. package/web/.next/static/chunks/e4522f7d3134eefe.js +1 -0
  234. package/web/.next/static/chunks/{d4ecce2ac7795bd9.js → fe83a799663fd3cc.js} +1 -1
  235. package/web/.next/trace +1 -1
  236. package/web/.next/trace-build +1 -1
  237. package/web/.next/server/chunks/ssr/_85d0351b._.js.map +0 -1
  238. package/web/.next/static/chunks/06b6b02bab76aee7.js +0 -1
  239. package/web/.next/static/chunks/06dfd668334ffb4e.js +0 -7
  240. package/web/.next/static/chunks/224ed5f5dbd33154.css +0 -2
  241. package/web/.next/static/chunks/e44849eb58a3335f.js +0 -1
  242. /package/web/.next/static/{7K6dTYQIJ7__PLUYLLfZV → 9bO_bJBssG0NRxEG03iM5}/_buildManifest.js +0 -0
  243. /package/web/.next/static/{7K6dTYQIJ7__PLUYLLfZV → 9bO_bJBssG0NRxEG03iM5}/_clientMiddlewareManifest.json +0 -0
  244. /package/web/.next/static/{7K6dTYQIJ7__PLUYLLfZV → 9bO_bJBssG0NRxEG03iM5}/_ssgManifest.js +0 -0
@@ -3,7 +3,7 @@ import { useState, useCallback, useEffect, useRef } from 'react';
3
3
  import { useRouter } from 'next/navigation';
4
4
  import { toast } from 'sonner';
5
5
  import { applyNodeChanges } from '@xyflow/react';
6
- import { layoutWithDagre } from '../../../lib/layout-with-dagre.js';
6
+ import { layoutWithDagre, CANVAS_LAYOUT_DEFAULTS, } from '../../../lib/layout-with-dagre.js';
7
7
  import { deleteFeature } from '../../../app/actions/delete-feature.js';
8
8
  import { addRepository } from '../../../app/actions/add-repository.js';
9
9
  import { deleteRepository } from '../../../app/actions/delete-repository.js';
@@ -11,9 +11,7 @@ import { useAgentEventsContext } from '../../../hooks/agent-events-provider.js';
11
11
  import { useSoundAction } from '../../../hooks/use-sound-action.js';
12
12
  import { mapEventTypeToState, mapPhaseNameToLifecycle, } from '../../common/feature-node/derive-feature-state.js';
13
13
  let nextFeatureId = 0;
14
- export function useControlCenterState(initialNodes, initialEdges,
15
- /** When true, background router.refresh() calls (polling / SSE debounce) are suppressed. */
16
- isRefreshBlocked) {
14
+ export function useControlCenterState(initialNodes, initialEdges) {
17
15
  const router = useRouter();
18
16
  const [nodes, setNodes] = useState(initialNodes);
19
17
  // eslint-disable-next-line react/hook-use-state -- raw setter renamed; public setEdges wrapper keeps edgesRef in sync
@@ -30,7 +28,7 @@ isRefreshBlocked) {
30
28
  }, []);
31
29
  const deleteSound = useSoundAction('delete');
32
30
  const createSound = useSoundAction('create');
33
- // Sync server props into local state when router.refresh() delivers new data
31
+ // Sync server props into local state when parent re-renders with new data
34
32
  const initialNodeKey = initialNodes
35
33
  .map((n) => n.id)
36
34
  .sort()
@@ -61,8 +59,9 @@ isRefreshBlocked) {
61
59
  const creatingNodes = currentNodes.filter((n) => n.type === 'featureNode' &&
62
60
  n.data.state === 'creating' &&
63
61
  !serverIds.has(n.id));
64
- // Merge server nodes with client positions
65
- return initialNodes.map((serverNode) => {
62
+ // Merge server nodes with client positions, tracking if relayout is needed
63
+ let needsRelayout = false;
64
+ const merged = initialNodes.map((serverNode) => {
66
65
  // Node already exists on canvas — keep its position, update data
67
66
  const existing = currentById.get(serverNode.id);
68
67
  if (existing) {
@@ -73,17 +72,28 @@ isRefreshBlocked) {
73
72
  const donor = creatingNodes.shift();
74
73
  return { ...serverNode, position: donor.position };
75
74
  }
76
- // Truly new node with no optimistic counterpart — use server position
75
+ // Truly new node with no optimistic counterpart — needs relayout
76
+ needsRelayout = true;
77
77
  return serverNode;
78
78
  });
79
+ // Also check if non-creating nodes were removed (graph got smaller)
80
+ if (!needsRelayout) {
81
+ needsRelayout = currentNodes.some((cn) => !serverIds.has(cn.id) &&
82
+ !(cn.type === 'featureNode' && cn.data.state === 'creating'));
83
+ }
84
+ if (needsRelayout) {
85
+ const layoutResult = layoutWithDagre(merged, initialEdges, CANVAS_LAYOUT_DEFAULTS);
86
+ setEdges(layoutResult.edges);
87
+ return layoutResult.nodes;
88
+ }
89
+ return merged;
79
90
  });
80
- }, [initialNodeKey, initialNodes]);
91
+ }, [initialNodeKey, initialNodes, initialEdges, setEdges]);
81
92
  useEffect(() => {
82
93
  setEdges(initialEdges);
83
94
  }, [initialEdgeKey, initialEdges, setEdges]);
84
- // Fallback notifications from server-refresh state transitions.
85
- // Fires only when SSE didn't already deliver the matching event for a feature,
86
- // e.g. when the SSE connection was down or the event was seeded into the cache.
95
+ // Fallback notifications from server-prop state transitions.
96
+ // Fires only when SSE didn't already deliver the matching event for a feature.
87
97
  const { events } = useAgentEventsContext();
88
98
  useEffect(() => {
89
99
  const prevStates = prevFeatureStatesRef.current;
@@ -118,14 +128,10 @@ isRefreshBlocked) {
118
128
  prevStates.set(node.id, data.state);
119
129
  }
120
130
  }, [initialDataKey, initialNodes, events, router]);
121
- // Targeted optimistic updates from SSE agent events + debounced reconciliation.
131
+ // Targeted optimistic updates from SSE agent events.
122
132
  // Uses `events` array (not `lastEvent`) so that React batching cannot silently
123
133
  // drop events when multiple SSE messages arrive in the same tick.
124
134
  const processedEventCountRef = useRef(0);
125
- const reconcileTimerRef = useRef(undefined);
126
- useEffect(() => {
127
- return () => clearTimeout(reconcileTimerRef.current);
128
- }, []);
129
135
  useEffect(() => {
130
136
  if (events.length <= processedEventCountRef.current)
131
137
  return;
@@ -151,31 +157,7 @@ isRefreshBlocked) {
151
157
  };
152
158
  }));
153
159
  }
154
- // Debounced background reconciliation (3s after last SSE event)
155
- clearTimeout(reconcileTimerRef.current);
156
- reconcileTimerRef.current = setTimeout(() => {
157
- if (!isRefreshBlocked?.())
158
- router.refresh();
159
- }, 3000);
160
- }, [events, router, isRefreshBlocked]);
161
- // Periodic polling fallback: refresh server data every 5s when any feature
162
- // is in an active state (running/action-required). This ensures the UI stays
163
- // current even if the SSE connection drops — belt-and-suspenders alongside SSE.
164
- useEffect(() => {
165
- const hasActiveFeature = nodes.some((n) => {
166
- if (n.type !== 'featureNode')
167
- return false;
168
- const data = n.data;
169
- return (data.state === 'running' || data.state === 'action-required' || data.state === 'creating');
170
- });
171
- if (!hasActiveFeature)
172
- return;
173
- const interval = setInterval(() => {
174
- if (!isRefreshBlocked?.())
175
- router.refresh();
176
- }, 5_000);
177
- return () => clearInterval(interval);
178
- }, [nodes, router, isRefreshBlocked]);
160
+ }, [events]);
179
161
  const onNodesChange = useCallback((changes) => {
180
162
  setNodes((ns) => applyNodeChanges(changes, ns));
181
163
  }, []);
@@ -220,202 +202,155 @@ isRefreshBlocked) {
220
202
  branch: dataOverride?.branch ?? '',
221
203
  };
222
204
  setNodes((currentNodes) => {
223
- // Read edges from ref to avoid closure dependency on `edges` state.
224
- // edgesRef is kept in sync by the setEdges wrapper.
225
- const siblingIds = sourceNodeId
226
- ? new Set(edgesRef.current.filter((e) => e.source === sourceNodeId).map((e) => e.target))
227
- : new Set();
228
- const siblings = currentNodes.filter((n) => siblingIds.has(n.id));
229
- let position;
230
- if (siblings.length > 0) {
231
- // Place below the bottom-most sibling, matching X
232
- const sortedYs = siblings.map((n) => n.position.y).sort((a, b) => a - b);
233
- const maxY = sortedYs[sortedYs.length - 1];
234
- // Derive gap from existing spacing between siblings, or use default
235
- const gap = sortedYs.length > 1 ? sortedYs[1] - sortedYs[0] : 160;
236
- position = { x: siblings[0].position.x, y: maxY + gap };
237
- }
238
- else if (sourceNodeId) {
239
- // First child — position to the right of parent
240
- const parent = currentNodes.find((n) => n.id === sourceNodeId);
241
- // Parent width (288) + 200px gap matching dagre ranksep
242
- const xOffset = 488;
243
- position = parent
244
- ? { x: parent.position.x + xOffset, y: parent.position.y }
245
- : { x: 400, y: 200 };
246
- }
247
- else {
248
- // Standalone feature — place below all existing nodes
249
- const maxY = currentNodes.length > 0 ? Math.max(...currentNodes.map((n) => n.position.y)) : 0;
250
- position = { x: 400, y: currentNodes.length > 0 ? maxY + 160 : 200 };
251
- }
252
- // The new node's bottom edge (featureNode height = 140)
253
- const newBottom = position.y + 140;
254
- // Find the old group bottom before adding the new node
255
- const groupNodeIds = new Set([sourceNodeId, ...siblingIds]);
256
- const oldGroupBottom = currentNodes
257
- .filter((n) => groupNodeIds.has(n.id))
258
- .reduce((max, n) => {
259
- const h = n.type === 'featureNode' ? 140 : 50;
260
- return Math.max(max, n.position.y + h);
261
- }, 0);
262
- // Shift amount: how much the group grew past its old bottom
263
- const shift = Math.max(0, newBottom - oldGroupBottom);
264
- // Push down all nodes that are below the old group bottom
265
- const shifted = shift > 0
266
- ? currentNodes.map((n) => {
267
- if (groupNodeIds.has(n.id) || siblingIds.has(n.id))
268
- return n;
269
- if (n.position.y >= oldGroupBottom) {
270
- return { ...n, position: { ...n.position, y: n.position.y + shift } };
271
- }
272
- return n;
273
- })
274
- : currentNodes;
275
- // Re-center the parent node vertically to its children
276
- const recentered = sourceNodeId
277
- ? shifted.map((n) => {
278
- if (n.id !== sourceNodeId)
279
- return n;
280
- const allChildYs = [...siblings.map((s) => s.position.y), position.y];
281
- const groupCenter = (Math.min(...allChildYs) + Math.max(...allChildYs) + 140) / 2;
282
- const parentHeight = n.type === 'featureNode' ? 140 : 50;
283
- return {
284
- ...n,
285
- position: { ...n.position, y: groupCenter - parentHeight / 2 },
286
- };
287
- })
288
- : shifted;
289
- return [
290
- ...recentered,
291
- {
292
- id,
293
- type: 'featureNode',
294
- position,
295
- data: newFeatureData,
296
- },
297
- ];
298
- });
299
- if (sourceNodeId) {
300
- setEdges((currentEdges) => [
301
- ...currentEdges,
302
- {
205
+ const newNode = {
206
+ id,
207
+ type: 'featureNode',
208
+ position: { x: 0, y: 0 },
209
+ data: newFeatureData,
210
+ };
211
+ const newEdge = sourceNodeId
212
+ ? {
303
213
  id: edgeType === 'dependencyEdge'
304
214
  ? `dep-${sourceNodeId}-${id}`
305
215
  : `edge-${sourceNodeId}-${id}`,
306
216
  source: sourceNodeId,
307
217
  target: id,
308
218
  ...(edgeType ? { type: edgeType } : { style: { strokeDasharray: '5 5' } }),
309
- },
310
- ]);
311
- }
219
+ }
220
+ : null;
221
+ const allEdges = newEdge
222
+ ? [...edgesRef.current.filter((e) => e.id !== newEdge.id), newEdge]
223
+ : edgesRef.current;
224
+ // Insert the new node after the last sibling connected to the same source,
225
+ // so dagre's input-order preservation keeps repo groups together.
226
+ let insertIndex = currentNodes.length;
227
+ if (sourceNodeId) {
228
+ const siblingIds = new Set(allEdges.filter((e) => e.source === sourceNodeId).map((e) => e.target));
229
+ for (let i = currentNodes.length - 1; i >= 0; i--) {
230
+ if (siblingIds.has(currentNodes[i].id)) {
231
+ insertIndex = i + 1;
232
+ break;
233
+ }
234
+ }
235
+ }
236
+ const allNodes = [
237
+ ...currentNodes.slice(0, insertIndex),
238
+ newNode,
239
+ ...currentNodes.slice(insertIndex),
240
+ ];
241
+ // Run dagre layout on the full graph so the new node is positioned consistently
242
+ const layoutResult = layoutWithDagre(allNodes, allEdges, CANVAS_LAYOUT_DEFAULTS);
243
+ setEdges(layoutResult.edges);
244
+ return layoutResult.nodes;
245
+ });
312
246
  return id;
313
247
  }, [setEdges]);
314
- const handleDeleteFeature = useCallback(async (featureId) => {
315
- try {
316
- const result = await deleteFeature(featureId);
248
+ const handleDeleteFeature = useCallback((featureId) => {
249
+ const nodeId = `feat-${featureId}`;
250
+ // Snapshot current state for rollback
251
+ const prevNodes = nodes;
252
+ const prevEdges = edgesRef.current;
253
+ // Optimistic removal — update UI immediately
254
+ setNodes((currentNodes) => {
255
+ const remainingNodes = currentNodes.filter((n) => n.id !== nodeId);
256
+ const remainingEdges = edgesRef.current.filter((e) => e.source !== nodeId && e.target !== nodeId);
257
+ const layoutResult = layoutWithDagre(remainingNodes, remainingEdges, CANVAS_LAYOUT_DEFAULTS);
258
+ setEdges(layoutResult.edges);
259
+ return layoutResult.nodes;
260
+ });
261
+ deleteSound.play();
262
+ toast.success('Feature deleted successfully');
263
+ router.push('/');
264
+ // Persist in background — rollback on failure
265
+ deleteFeature(featureId)
266
+ .then((result) => {
317
267
  if (result.error) {
268
+ setNodes(prevNodes);
269
+ setEdges(prevEdges);
318
270
  toast.error(result.error);
319
- return;
320
271
  }
321
- setNodes((currentNodes) => {
322
- const remainingNodes = currentNodes.filter((n) => n.id !== featureId);
323
- const remainingEdges = edges.filter((e) => e.source !== featureId && e.target !== featureId);
324
- const layoutResult = layoutWithDagre(remainingNodes, remainingEdges, {
325
- direction: 'LR',
326
- ranksep: 200,
327
- nodesep: 60,
328
- });
329
- setEdges(layoutResult.edges);
330
- return layoutResult.nodes;
331
- });
332
- deleteSound.play();
333
- toast.success('Feature deleted successfully');
334
- router.refresh();
335
- }
336
- catch {
272
+ })
273
+ .catch(() => {
274
+ setNodes(prevNodes);
275
+ setEdges(prevEdges);
337
276
  toast.error('Failed to delete feature');
338
- }
339
- }, [router, edges, deleteSound, setEdges]);
277
+ });
278
+ }, [nodes, router, deleteSound, setEdges]);
340
279
  const handleDeleteRepository = useCallback(async (repositoryId) => {
341
280
  const repoNodeId = `repo-${repositoryId}`;
342
- // Optimistic: remove node and its edges immediately
343
- setNodes((prev) => prev.filter((n) => n.id !== repoNodeId));
344
- setEdges((prev) => prev.filter((e) => e.source !== repoNodeId && e.target !== repoNodeId));
281
+ // Snapshot for rollback
282
+ const prevNodes = nodes;
283
+ const prevEdges = edgesRef.current;
284
+ // Optimistic: remove repo node, its child feature nodes, and all related edges
285
+ setNodes((currentNodes) => {
286
+ // Find feature node IDs connected to this repo via edges
287
+ const childFeatureIds = new Set(edgesRef.current.filter((e) => e.source === repoNodeId).map((e) => e.target));
288
+ const remainingNodes = currentNodes.filter((n) => n.id !== repoNodeId && !childFeatureIds.has(n.id));
289
+ const remainingEdges = edgesRef.current.filter((e) => e.source !== repoNodeId &&
290
+ e.target !== repoNodeId &&
291
+ !childFeatureIds.has(e.source) &&
292
+ !childFeatureIds.has(e.target));
293
+ const layoutResult = layoutWithDagre(remainingNodes, remainingEdges, CANVAS_LAYOUT_DEFAULTS);
294
+ setEdges(layoutResult.edges);
295
+ return layoutResult.nodes;
296
+ });
345
297
  try {
346
298
  const result = await deleteRepository(repositoryId);
347
299
  if (!result.success) {
348
300
  toast.error(result.error ?? 'Failed to remove repository');
349
- router.refresh();
301
+ setNodes(prevNodes);
302
+ setEdges(prevEdges);
350
303
  return;
351
304
  }
352
305
  deleteSound.play();
353
306
  toast.success('Repository removed');
354
- router.refresh();
355
307
  }
356
308
  catch {
357
309
  toast.error('Failed to remove repository');
358
- router.refresh();
310
+ setNodes(prevNodes);
311
+ setEdges(prevEdges);
359
312
  }
360
- }, [router, deleteSound, setEdges]);
313
+ }, [nodes, deleteSound, setEdges]);
361
314
  const handleLayout = useCallback((direction) => {
362
315
  setNodes((currentNodes) => {
363
- const currentEdges = edges;
364
- const result = layoutWithDagre(currentNodes, currentEdges, {
316
+ const result = layoutWithDagre(currentNodes, edgesRef.current, {
317
+ ...CANVAS_LAYOUT_DEFAULTS,
365
318
  direction,
366
- ranksep: 60,
367
- nodesep: 20,
368
319
  });
369
320
  setEdges(result.edges);
370
321
  return result.nodes;
371
322
  });
372
- }, [edges, setEdges]);
323
+ }, [setEdges]);
373
324
  const handleAddRepository = useCallback((path) => {
374
325
  const tempId = `repo-temp-${Date.now()}`;
375
326
  const repoName = path
376
327
  .replace(/[\\/]+$/, '')
377
328
  .split(/[\\/]/)
378
329
  .pop() ?? path;
379
- // Optimistic UI: add node immediately
380
- let savedAddRepoY = 0;
330
+ // Optimistic UI: add node and re-layout immediately
381
331
  setNodes((currentNodes) => {
382
- const repoNodes = currentNodes.filter((n) => n.type === 'repositoryNode');
383
- const addRepoNode = currentNodes.find((n) => n.type === 'addRepositoryNode');
384
- // Save addRepoNode's original Y for rollback on error
385
- if (addRepoNode)
386
- savedAddRepoY = addRepoNode.position.y;
387
- // Place in the repo column, at the addRepoNode's current position
388
- const repoX = repoNodes[0]?.position.x ?? addRepoNode?.position.x ?? 50;
389
- const repoHeight = 50; // repositoryNode height
390
- const gap = 15; // match dagre nodesep
391
- const position = {
392
- x: repoX,
393
- y: addRepoNode ? addRepoNode.position.y : 0,
394
- };
395
332
  const newNode = {
396
333
  id: tempId,
397
334
  type: 'repositoryNode',
398
- position,
335
+ position: { x: 0, y: 0 },
399
336
  data: { name: repoName, repositoryPath: path, id: tempId },
400
337
  };
401
- // Shift addRepo button down by exactly one slot
402
- const addRepoY = addRepoNode
403
- ? addRepoNode.position.y + repoHeight + gap
404
- : position.y + repoHeight + gap;
405
- return currentNodes
406
- .map((n) => n.type === 'addRepositoryNode' ? { ...n, position: { ...n.position, y: addRepoY } } : n)
407
- .concat(newNode);
338
+ const allNodes = [...currentNodes, newNode];
339
+ const layoutResult = layoutWithDagre(allNodes, edgesRef.current, CANVAS_LAYOUT_DEFAULTS);
340
+ setEdges(layoutResult.edges);
341
+ return layoutResult.nodes;
408
342
  });
409
343
  // Persist via server action
410
344
  addRepository({ path, name: repoName })
411
345
  .then((result) => {
412
346
  if (result.error) {
413
- // Rollback optimistic node and restore addRepoNode position
414
- setNodes((prev) => prev
415
- .filter((n) => n.id !== tempId)
416
- .map((n) => n.type === 'addRepositoryNode'
417
- ? { ...n, position: { ...n.position, y: savedAddRepoY } }
418
- : n));
347
+ // Rollback optimistic node and re-layout
348
+ setNodes((prev) => {
349
+ const remaining = prev.filter((n) => n.id !== tempId);
350
+ const layoutResult = layoutWithDagre(remaining, edgesRef.current, CANVAS_LAYOUT_DEFAULTS);
351
+ setEdges(layoutResult.edges);
352
+ return layoutResult.nodes;
353
+ });
419
354
  toast.error(result.error);
420
355
  return;
421
356
  }
@@ -436,18 +371,18 @@ isRefreshBlocked) {
436
371
  id: e.id.replace(tempId, realId),
437
372
  })));
438
373
  createSound.play();
439
- router.refresh();
440
374
  })
441
375
  .catch(() => {
442
- // Rollback optimistic node and restore addRepoNode position
443
- setNodes((prev) => prev
444
- .filter((n) => n.id !== tempId)
445
- .map((n) => n.type === 'addRepositoryNode'
446
- ? { ...n, position: { ...n.position, y: savedAddRepoY } }
447
- : n));
376
+ // Rollback optimistic node and re-layout
377
+ setNodes((prev) => {
378
+ const remaining = prev.filter((n) => n.id !== tempId);
379
+ const layoutResult = layoutWithDagre(remaining, edgesRef.current, CANVAS_LAYOUT_DEFAULTS);
380
+ setEdges(layoutResult.edges);
381
+ return layoutResult.nodes;
382
+ });
448
383
  toast.error('Failed to add repository');
449
384
  });
450
- }, [router, createSound, setEdges]);
385
+ }, [createSound, setEdges]);
451
386
  return {
452
387
  nodes,
453
388
  edges,
@@ -4,7 +4,7 @@ import { Button } from '../ui/button.js';
4
4
  declare function AlertDialog({ ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Root>): import("react/jsx-runtime").JSX.Element;
5
5
  declare function AlertDialogTrigger({ ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>): import("react/jsx-runtime").JSX.Element;
6
6
  declare function AlertDialogPortal({ ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Portal>): import("react/jsx-runtime").JSX.Element;
7
- declare function AlertDialogOverlay({ className, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>): import("react/jsx-runtime").JSX.Element;
7
+ declare function AlertDialogOverlay({ className, onClick, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>): import("react/jsx-runtime").JSX.Element;
8
8
  declare function AlertDialogContent({ className, size, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Content> & {
9
9
  size?: 'default' | 'sm';
10
10
  }): import("react/jsx-runtime").JSX.Element;
@@ -1 +1 @@
1
- {"version":3,"file":"alert-dialog.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/components/ui/alert-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,WAAW,IAAI,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAG/D,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAEhD,iBAAS,WAAW,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,IAAI,CAAC,2CAExF;AAED,iBAAS,kBAAkB,CAAC,EAC1B,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,OAAO,CAAC,2CAE3D;AAED,iBAAS,iBAAiB,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,MAAM,CAAC,2CAEhG;AAED,iBAAS,kBAAkB,CAAC,EAC1B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,OAAO,CAAC,2CAW3D;AAED,iBAAS,kBAAkB,CAAC,EAC1B,SAAS,EACT,IAAgB,EAChB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,OAAO,CAAC,GAAG;IAC7D,IAAI,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;CACzB,2CAeA;AAED,iBAAS,iBAAiB,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,2CAW9E;AAED,iBAAS,iBAAiB,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,2CAW9E;AAED,iBAAS,gBAAgB,CAAC,EACxB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,KAAK,CAAC,2CAWzD;AAED,iBAAS,sBAAsB,CAAC,EAC9B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,WAAW,CAAC,2CAQ/D;AAED,iBAAS,gBAAgB,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,2CAW7E;AAED,iBAAS,iBAAiB,CAAC,EACzB,SAAS,EACT,OAAmB,EACnB,IAAgB,EAChB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,MAAM,CAAC,GACzD,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,MAAM,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC,2CAU9D;AAED,iBAAS,iBAAiB,CAAC,EACzB,SAAS,EACT,OAAmB,EACnB,IAAgB,EAChB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,MAAM,CAAC,GACzD,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,MAAM,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC,2CAU9D;AAED,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,sBAAsB,EACtB,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,GACnB,CAAC"}
1
+ {"version":3,"file":"alert-dialog.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/components/ui/alert-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,WAAW,IAAI,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAG/D,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAEhD,iBAAS,WAAW,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,IAAI,CAAC,2CAExF;AAED,iBAAS,kBAAkB,CAAC,EAC1B,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,OAAO,CAAC,2CAE3D;AAED,iBAAS,iBAAiB,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,MAAM,CAAC,2CAEhG;AAED,iBAAS,kBAAkB,CAAC,EAC1B,SAAS,EACT,OAAO,EACP,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,OAAO,CAAC,2CAiB3D;AAED,iBAAS,kBAAkB,CAAC,EAC1B,SAAS,EACT,IAAgB,EAChB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,OAAO,CAAC,GAAG;IAC7D,IAAI,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;CACzB,2CAeA;AAED,iBAAS,iBAAiB,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,2CAW9E;AAED,iBAAS,iBAAiB,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,2CAW9E;AAED,iBAAS,gBAAgB,CAAC,EACxB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,KAAK,CAAC,2CAWzD;AAED,iBAAS,sBAAsB,CAAC,EAC9B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,WAAW,CAAC,2CAQ/D;AAED,iBAAS,gBAAgB,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,2CAW7E;AAED,iBAAS,iBAAiB,CAAC,EACzB,SAAS,EACT,OAAmB,EACnB,IAAgB,EAChB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,MAAM,CAAC,GACzD,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,MAAM,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC,2CAU9D;AAED,iBAAS,iBAAiB,CAAC,EACzB,SAAS,EACT,OAAmB,EACnB,IAAgB,EAChB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,MAAM,CAAC,GACzD,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,MAAM,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC,2CAU9D;AAED,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,sBAAsB,EACtB,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,GACnB,CAAC"}
@@ -12,8 +12,13 @@ function AlertDialogTrigger({ ...props }) {
12
12
  function AlertDialogPortal({ ...props }) {
13
13
  return _jsx(AlertDialogPrimitive.Portal, { "data-slot": "alert-dialog-portal", ...props });
14
14
  }
15
- function AlertDialogOverlay({ className, ...props }) {
16
- return (_jsx(AlertDialogPrimitive.Overlay, { "data-slot": "alert-dialog-overlay", className: cn('data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50', className), ...props }));
15
+ function AlertDialogOverlay({ className, onClick, ...props }) {
16
+ return (_jsx(AlertDialogPrimitive.Overlay, { "data-slot": "alert-dialog-overlay", className: cn('data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50', className), onClick: (e) => {
17
+ // Prevent overlay clicks from propagating to document-level handlers
18
+ // (e.g. BaseDrawer outside-click) — only the dialog should dismiss.
19
+ e.stopPropagation();
20
+ onClick?.(e);
21
+ }, ...props }));
17
22
  }
18
23
  function AlertDialogContent({ className, size = 'default', ...props }) {
19
24
  return (_jsxs(AlertDialogPortal, { children: [_jsx(AlertDialogOverlay, {}), _jsx(AlertDialogPrimitive.Content, { "data-slot": "alert-dialog-content", "data-size": size, className: cn('bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 group/alert-dialog-content fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-lg', className), ...props })] }));
@@ -1 +1 @@
1
- {"version":3,"file":"dialog.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/components/ui/dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,MAAM,IAAI,eAAe,EAAE,MAAM,UAAU,CAAC;AAKrD,QAAA,MAAM,MAAM,uCAAuB,CAAC;AAEpC,QAAA,MAAM,aAAa,8GAA0B,CAAC;AAE9C,QAAA,MAAM,YAAY,6CAAyB,CAAC;AAE5C,QAAA,MAAM,WAAW,4GAAwB,CAAC;AAE1C,QAAA,MAAM,aAAa,8JAYjB,CAAC;AAGH,QAAA,MAAM,aAAa,8JAqBjB,CAAC;AAGH,QAAA,MAAM,YAAY;8BAA6B,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;;CAElF,CAAC;AAGF,QAAA,MAAM,YAAY;8BAA6B,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;;CAKlF,CAAC;AAGF,QAAA,MAAM,WAAW,oKASf,CAAC;AAGH,QAAA,MAAM,iBAAiB,8KASrB,CAAC;AAGH,OAAO,EACL,MAAM,EACN,YAAY,EACZ,aAAa,EACb,aAAa,EACb,WAAW,EACX,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,iBAAiB,GAClB,CAAC"}
1
+ {"version":3,"file":"dialog.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/components/ui/dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,MAAM,IAAI,eAAe,EAAE,MAAM,UAAU,CAAC;AAKrD,QAAA,MAAM,MAAM,uCAAuB,CAAC;AAEpC,QAAA,MAAM,aAAa,8GAA0B,CAAC;AAE9C,QAAA,MAAM,YAAY,6CAAyB,CAAC;AAE5C,QAAA,MAAM,WAAW,4GAAwB,CAAC;AAE1C,QAAA,MAAM,aAAa,8JAkBjB,CAAC;AAGH,QAAA,MAAM,aAAa,8JAqBjB,CAAC;AAGH,QAAA,MAAM,YAAY;8BAA6B,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;;CAElF,CAAC;AAGF,QAAA,MAAM,YAAY;8BAA6B,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;;CAKlF,CAAC;AAGF,QAAA,MAAM,WAAW,oKASf,CAAC;AAGH,QAAA,MAAM,iBAAiB,8KASrB,CAAC;AAGH,OAAO,EACL,MAAM,EACN,YAAY,EACZ,aAAa,EACb,aAAa,EACb,WAAW,EACX,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,iBAAiB,GAClB,CAAC"}
@@ -8,7 +8,12 @@ const Dialog = DialogPrimitive.Root;
8
8
  const DialogTrigger = DialogPrimitive.Trigger;
9
9
  const DialogPortal = DialogPrimitive.Portal;
10
10
  const DialogClose = DialogPrimitive.Close;
11
- const DialogOverlay = React.forwardRef(({ className, ...props }, ref) => (_jsx(DialogPrimitive.Overlay, { ref: ref, className: cn('data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80', className), ...props })));
11
+ const DialogOverlay = React.forwardRef(({ className, onClick, ...props }, ref) => (_jsx(DialogPrimitive.Overlay, { ref: ref, className: cn('data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80', className), onClick: (e) => {
12
+ // Prevent overlay clicks from propagating to document-level handlers
13
+ // (e.g. BaseDrawer outside-click) — only the dialog should dismiss.
14
+ e.stopPropagation();
15
+ onClick?.(e);
16
+ }, ...props })));
12
17
  DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
13
18
  const DialogContent = React.forwardRef(({ className, children, ...props }, ref) => (_jsxs(DialogPortal, { children: [_jsx(DialogOverlay, {}), _jsxs(DialogPrimitive.Content, { ref: ref, className: cn('bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] fixed top-[50%] left-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border p-6 shadow-lg duration-200 sm:rounded-lg', className), ...props, children: [children, _jsxs(DialogPrimitive.Close, { className: "ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-none disabled:pointer-events-none", children: [_jsx(X, { className: "h-4 w-4" }), _jsx("span", { className: "sr-only", children: "Close" })] })] })] })));
14
19
  DialogContent.displayName = DialogPrimitive.Content.displayName;
@@ -13,6 +13,8 @@ export interface LayoutOptions {
13
13
  /** Horizontal separation between nodes in the same rank (default 30) */
14
14
  nodesep?: number;
15
15
  }
16
+ /** Canonical layout defaults for the control-center canvas. */
17
+ export declare const CANVAS_LAYOUT_DEFAULTS: LayoutOptions;
16
18
  /**
17
19
  * Compute an automatic hierarchical layout for React Flow nodes and edges
18
20
  * using Dagre. Returns new node/edge arrays — never mutates the originals.
@@ -1 +1 @@
1
- {"version":3,"file":"layout-with-dagre.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/lib/layout-with-dagre.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAEhD,MAAM,MAAM,eAAe,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAExD,MAAM,WAAW,aAAa;IAC5B,iEAAiE;IACjE,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,0DAA0D;IAC1D,QAAQ,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C,qDAAqD;IACrD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAmCD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,IAAI,EAC5C,KAAK,EAAE,CAAC,EAAE,EACV,KAAK,EAAE,IAAI,EAAE,EACb,IAAI,GAAE,aAAkB,GACvB;IAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IAAC,KAAK,EAAE,IAAI,EAAE,CAAA;CAAE,CAkF/B"}
1
+ {"version":3,"file":"layout-with-dagre.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/lib/layout-with-dagre.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAEhD,MAAM,MAAM,eAAe,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAExD,MAAM,WAAW,aAAa;IAC5B,iEAAiE;IACjE,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,0DAA0D;IAC1D,QAAQ,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C,qDAAqD;IACrD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,+DAA+D;AAC/D,eAAO,MAAM,sBAAsB,EAAE,aAIpC,CAAC;AAmCF;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,IAAI,EAC5C,KAAK,EAAE,CAAC,EAAE,EACV,KAAK,EAAE,IAAI,EAAE,EACb,IAAI,GAAE,aAAkB,GACvB;IAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IAAC,KAAK,EAAE,IAAI,EAAE,CAAA;CAAE,CAoK/B"}
@@ -1,4 +1,10 @@
1
1
  import dagre from '@dagrejs/dagre';
2
+ /** Canonical layout defaults for the control-center canvas. */
3
+ export const CANVAS_LAYOUT_DEFAULTS = {
4
+ direction: 'LR',
5
+ ranksep: 200,
6
+ nodesep: 15,
7
+ };
2
8
  /** Known node-type dimensions for the canvas node types */
3
9
  const NODE_DIMENSIONS = {
4
10
  featureNode: { width: 288, height: 140 },
@@ -62,16 +68,92 @@ export function layoutWithDagre(nodes, edges, opts = {}) {
62
68
  }
63
69
  dagre.layout(g);
64
70
  const { targetPosition, sourcePosition } = getHandlePositions(direction);
65
- const result = [];
66
- // Map laid-out nodes, converting dagre center-coords to React Flow top-left
71
+ const isHorizontal = direction === 'LR' || direction === 'RL';
72
+ // Collect dagre center positions keyed by node id
73
+ const centerMap = new Map();
67
74
  for (const node of graphNodes) {
68
- const pos = g.node(node.id);
75
+ const dagreNode = g.node(node.id);
69
76
  const size = getNodeSize(node, nodeSize);
77
+ centerMap.set(node.id, { cx: dagreNode.x, cy: dagreNode.y, w: size.width, h: size.height });
78
+ }
79
+ // Post-process: center each group of children around their parent's
80
+ // secondary-axis center so the edge fans out symmetrically.
81
+ // Build parent→children map from the edges.
82
+ const childrenOf = new Map();
83
+ for (const edge of edges) {
84
+ if (!centerMap.has(edge.source) || !centerMap.has(edge.target))
85
+ continue;
86
+ if (!childrenOf.has(edge.source))
87
+ childrenOf.set(edge.source, []);
88
+ childrenOf.get(edge.source).push(edge.target);
89
+ }
90
+ for (const [parentId, childIds] of childrenOf) {
91
+ if (childIds.length <= 1)
92
+ continue;
93
+ const parent = centerMap.get(parentId);
94
+ const parentCenter = isHorizontal ? parent.cy : parent.cx;
95
+ // Sort children by their current secondary-axis position
96
+ const sorted = childIds
97
+ .filter((id) => centerMap.has(id))
98
+ .sort((a, b) => {
99
+ const pa = centerMap.get(a);
100
+ const pb = centerMap.get(b);
101
+ return isHorizontal ? pa.cy - pb.cy : pa.cx - pb.cx;
102
+ });
103
+ // Compute the current center of the children group
104
+ const first = centerMap.get(sorted[0]);
105
+ const last = centerMap.get(sorted[sorted.length - 1]);
106
+ const groupCenter = isHorizontal ? (first.cy + last.cy) / 2 : (first.cx + last.cx) / 2;
107
+ // Shift all children so the group center aligns with the parent center
108
+ const shift = parentCenter - groupCenter;
109
+ if (Math.abs(shift) > 1) {
110
+ for (const childId of sorted) {
111
+ const c = centerMap.get(childId);
112
+ if (isHorizontal) {
113
+ c.cy += shift;
114
+ }
115
+ else {
116
+ c.cx += shift;
117
+ }
118
+ }
119
+ }
120
+ }
121
+ // Enforce input-order within each sibling group so that the caller's
122
+ // sort (e.g. by createdAt) is respected on the secondary axis.
123
+ const inputIndex = new Map();
124
+ for (let i = 0; i < graphNodes.length; i++) {
125
+ inputIndex.set(graphNodes[i].id, i);
126
+ }
127
+ for (const [, childIds] of childrenOf) {
128
+ const valid = childIds.filter((id) => centerMap.has(id));
129
+ if (valid.length <= 1)
130
+ continue;
131
+ // Collect current secondary-axis positions, sorted ascending
132
+ const positions = valid
133
+ .map((id) => (isHorizontal ? centerMap.get(id).cy : centerMap.get(id).cx))
134
+ .sort((a, b) => a - b);
135
+ // Sort children by input index (preserves caller's ordering, e.g. createdAt)
136
+ const byInput = [...valid].sort((a, b) => (inputIndex.get(a) ?? 0) - (inputIndex.get(b) ?? 0));
137
+ // Assign sorted positions in input order
138
+ for (let i = 0; i < byInput.length; i++) {
139
+ const c = centerMap.get(byInput[i]);
140
+ if (isHorizontal) {
141
+ c.cy = positions[i];
142
+ }
143
+ else {
144
+ c.cx = positions[i];
145
+ }
146
+ }
147
+ }
148
+ // Build result array converting center-coords to React Flow top-left
149
+ const result = [];
150
+ for (const node of graphNodes) {
151
+ const c = centerMap.get(node.id);
70
152
  result.push({
71
153
  ...node,
72
154
  targetPosition,
73
155
  sourcePosition,
74
- position: { x: pos.x - size.width / 2, y: pos.y - size.height / 2 },
156
+ position: { x: c.cx - c.w / 2, y: c.cy - c.h / 2 },
75
157
  });
76
158
  }
77
159
  // Place disconnected nodes after the laid-out graph so they don't overlap