@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.
- package/dist/packages/core/src/application/use-cases/features/delete-feature.use-case.d.ts +4 -0
- package/dist/packages/core/src/application/use-cases/features/delete-feature.use-case.d.ts.map +1 -1
- package/dist/packages/core/src/application/use-cases/features/delete-feature.use-case.js +20 -12
- package/dist/packages/core/src/application/use-cases/repositories/delete-repository.use-case.d.ts +8 -3
- package/dist/packages/core/src/application/use-cases/repositories/delete-repository.use-case.d.ts.map +1 -1
- package/dist/packages/core/src/application/use-cases/repositories/delete-repository.use-case.js +17 -4
- package/dist/packages/core/src/infrastructure/repositories/sqlite-feature.repository.js +1 -1
- package/dist/src/presentation/web/app/(dashboard)/get-graph-data.d.ts.map +1 -1
- package/dist/src/presentation/web/app/(dashboard)/get-graph-data.js +2 -6
- package/dist/src/presentation/web/app/build-graph-nodes.js +11 -1
- package/dist/src/presentation/web/components/common/base-drawer/base-drawer.d.ts +3 -1
- package/dist/src/presentation/web/components/common/base-drawer/base-drawer.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/base-drawer/base-drawer.js +8 -4
- package/dist/src/presentation/web/components/common/control-center-drawer/create-drawer-client.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/control-center-drawer/create-drawer-client.js +12 -3
- package/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.js +6 -2
- package/dist/src/presentation/web/components/common/drawer-action-bar/drawer-action-bar.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/drawer-action-bar/drawer-action-bar.js +1 -1
- package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.js +1 -1
- package/dist/src/presentation/web/components/common/feature-node/feature-node.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-node/feature-node.js +7 -2
- package/dist/src/presentation/web/components/common/repository-node/repository-node.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/repository-node/repository-node.js +6 -2
- package/dist/src/presentation/web/components/features/control-center/control-center-inner.d.ts.map +1 -1
- package/dist/src/presentation/web/components/features/control-center/control-center-inner.js +14 -2
- package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts +3 -5
- package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts.map +1 -1
- package/dist/src/presentation/web/components/features/control-center/use-control-center-state.js +131 -196
- package/dist/src/presentation/web/components/ui/alert-dialog.d.ts +1 -1
- package/dist/src/presentation/web/components/ui/alert-dialog.d.ts.map +1 -1
- package/dist/src/presentation/web/components/ui/alert-dialog.js +7 -2
- package/dist/src/presentation/web/components/ui/dialog.d.ts.map +1 -1
- package/dist/src/presentation/web/components/ui/dialog.js +6 -1
- package/dist/src/presentation/web/lib/layout-with-dagre.d.ts +2 -0
- package/dist/src/presentation/web/lib/layout-with-dagre.d.ts.map +1 -1
- package/dist/src/presentation/web/lib/layout-with-dagre.js +86 -4
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/build-manifest.json +2 -2
- package/web/.next/cache/.previewinfo +1 -1
- package/web/.next/cache/.rscinfo +1 -1
- package/web/.next/cache/.tsbuildinfo +1 -1
- package/web/.next/cache/config.json +3 -3
- package/web/.next/fallback-build-manifest.json +2 -2
- package/web/.next/prerender-manifest.json +3 -3
- package/web/.next/required-server-files.js +1 -1
- package/web/.next/required-server-files.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +14 -14
- package/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +17 -17
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +12 -12
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +14 -14
- package/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +17 -17
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +12 -12
- package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +12 -12
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/_global-error.html +2 -2
- package/web/.next/server/app/_global-error.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/skills/page/server-reference-manifest.json +6 -6
- package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/tools/page/server-reference-manifest.json +1 -1
- package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
- package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
- package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
- package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js.map +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_2b7834f6.js +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_2b7834f6.js.map +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_6923c575.js +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_6923c575.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__07385073._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__07385073._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__08ba9bd3._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__08ba9bd3._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__249c74f6._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__6bb00d90._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__6bb00d90._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__6db85a66._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__8fcb9132._.js +2 -2
- package/web/.next/server/chunks/ssr/_00e6c698._.js +1 -1
- package/web/.next/server/chunks/ssr/_00e6c698._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_07c2a32a._.js +1 -1
- package/web/.next/server/chunks/ssr/_07c2a32a._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_34a58db8._.js +1 -1
- package/web/.next/server/chunks/ssr/_34a58db8._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_37259405._.js +1 -1
- package/web/.next/server/chunks/ssr/_37259405._.js.map +1 -1
- package/web/.next/server/chunks/ssr/{_85d0351b._.js → _63bfa7dd._.js} +2 -2
- package/web/.next/server/chunks/ssr/_63bfa7dd._.js.map +1 -0
- package/web/.next/{standalone/src/presentation/web/.next/server/chunks/ssr/_e50484ec._.js → server/chunks/ssr/_7215bb0f._.js} +2 -2
- package/web/.next/server/chunks/ssr/{_e50484ec._.js.map → _7215bb0f._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/_cb5b3951._.js +1 -1
- package/web/.next/server/chunks/ssr/_cb5b3951._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_fc3a6895._.js +1 -1
- package/web/.next/server/chunks/ssr/_fc3a6895._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_1a32dbcb._.js +2 -2
- package/web/.next/server/chunks/ssr/src_presentation_web_1a32dbcb._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_398553b0._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_398553b0._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_5c87e8e4._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_5c87e8e4._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_f4952172.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_f4952172.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_c1bc6240._.js +2 -2
- package/web/.next/server/chunks/ssr/src_presentation_web_c1bc6240._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_ca99d62d._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_ca99d62d._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js.map +1 -1
- package/web/.next/server/pages/500.html +2 -2
- package/web/.next/server/server-reference-manifest.js +1 -1
- package/web/.next/server/server-reference-manifest.json +20 -20
- package/web/.next/standalone/src/presentation/web/.next/BUILD_ID +1 -1
- package/web/.next/standalone/src/presentation/web/.next/build-manifest.json +2 -2
- package/web/.next/standalone/src/presentation/web/.next/prerender-manifest.json +3 -3
- package/web/.next/standalone/src/presentation/web/.next/required-server-files.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +14 -14
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +17 -17
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +12 -12
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +14 -14
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +17 -17
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +12 -12
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +12 -12
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.html +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page/server-reference-manifest.json +6 -6
- package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page/server-reference-manifest.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
- 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
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_2b7834f6.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_6923c575.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__07385073._.js +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__08ba9bd3._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__249c74f6._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__6bb00d90._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__6db85a66._.js +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__8fcb9132._.js +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_00e6c698._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_07c2a32a._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_34a58db8._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_37259405._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/{_85d0351b._.js → _63bfa7dd._.js} +2 -2
- package/web/.next/{server/chunks/ssr/_e50484ec._.js → standalone/src/presentation/web/.next/server/chunks/ssr/_7215bb0f._.js} +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_cb5b3951._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_fc3a6895._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_1a32dbcb._.js +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_398553b0._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_5c87e8e4._.js +1 -1
- 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
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_c1bc6240._.js +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_ca99d62d._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/pages/500.html +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.json +20 -20
- package/web/.next/standalone/src/presentation/web/app/(dashboard)/get-graph-data.ts +2 -6
- package/web/.next/standalone/src/presentation/web/app/build-graph-nodes.ts +14 -1
- package/web/.next/standalone/src/presentation/web/components/common/base-drawer/base-drawer.tsx +10 -8
- package/web/.next/standalone/src/presentation/web/components/common/control-center-drawer/create-drawer-client.tsx +13 -3
- package/web/.next/standalone/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.tsx +6 -2
- package/web/.next/standalone/src/presentation/web/components/common/drawer-action-bar/drawer-action-bar.tsx +2 -0
- package/web/.next/standalone/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.tsx +8 -5
- package/web/.next/standalone/src/presentation/web/components/common/feature-node/feature-node.tsx +30 -24
- package/web/.next/standalone/src/presentation/web/components/common/repository-node/repository-node.tsx +33 -25
- package/web/.next/standalone/src/presentation/web/components/features/control-center/control-center-inner.tsx +25 -3
- package/web/.next/standalone/src/presentation/web/components/features/control-center/use-control-center-state.ts +184 -230
- package/web/.next/standalone/src/presentation/web/components/ui/alert-dialog.tsx +7 -0
- package/web/.next/standalone/src/presentation/web/components/ui/dialog.tsx +7 -1
- package/web/.next/standalone/src/presentation/web/lib/layout-with-dagre.ts +93 -4
- package/web/.next/standalone/src/presentation/web/server.js +1 -1
- package/web/.next/static/chunks/0c54314c8c9c9739.js +7 -0
- package/web/.next/static/chunks/{5f5c607ffae8b918.js → 304c8593d0a6525d.js} +1 -1
- package/web/.next/static/chunks/4558d1663c6f4d2c.js +1 -0
- package/web/.next/static/chunks/{32957ff277daec59.js → 4df07e765b233622.js} +1 -1
- package/web/.next/static/chunks/{fc07834e422b1caa.js → 585ae19c520e8b72.js} +1 -1
- package/web/.next/static/chunks/{ecb2130572a7fbe7.js → 6e14d8bd5c005e6e.js} +1 -1
- package/web/.next/static/chunks/{412a3ac70c529c5b.js → 8a1eb93510e54db0.js} +1 -1
- package/web/.next/static/chunks/{550bf3cb8220cbf0.js → b143c7d78a6888e9.js} +1 -1
- package/web/.next/static/chunks/{8b36e07129a1471f.js → b45344579a43cbb8.js} +1 -1
- package/web/.next/static/chunks/dec13bccb136ef7c.css +2 -0
- package/web/.next/static/chunks/e4522f7d3134eefe.js +1 -0
- package/web/.next/static/chunks/{d4ecce2ac7795bd9.js → fe83a799663fd3cc.js} +1 -1
- package/web/.next/trace +1 -1
- package/web/.next/trace-build +1 -1
- package/web/.next/server/chunks/ssr/_85d0351b._.js.map +0 -1
- package/web/.next/static/chunks/06b6b02bab76aee7.js +0 -1
- package/web/.next/static/chunks/06dfd668334ffb4e.js +0 -7
- package/web/.next/static/chunks/224ed5f5dbd33154.css +0 -2
- package/web/.next/static/chunks/e44849eb58a3335f.js +0 -1
- /package/web/.next/static/{7K6dTYQIJ7__PLUYLLfZV → 9bO_bJBssG0NRxEG03iM5}/_buildManifest.js +0 -0
- /package/web/.next/static/{7K6dTYQIJ7__PLUYLLfZV → 9bO_bJBssG0NRxEG03iM5}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{7K6dTYQIJ7__PLUYLLfZV → 9bO_bJBssG0NRxEG03iM5}/_ssgManifest.js +0 -0
package/dist/src/presentation/web/components/features/control-center/use-control-center-state.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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 —
|
|
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-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
:
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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(
|
|
315
|
-
|
|
316
|
-
|
|
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
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
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
|
-
}, [
|
|
277
|
+
});
|
|
278
|
+
}, [nodes, router, deleteSound, setEdges]);
|
|
340
279
|
const handleDeleteRepository = useCallback(async (repositoryId) => {
|
|
341
280
|
const repoNodeId = `repo-${repositoryId}`;
|
|
342
|
-
//
|
|
343
|
-
|
|
344
|
-
|
|
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
|
-
|
|
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
|
-
|
|
310
|
+
setNodes(prevNodes);
|
|
311
|
+
setEdges(prevEdges);
|
|
359
312
|
}
|
|
360
|
-
}, [
|
|
313
|
+
}, [nodes, deleteSound, setEdges]);
|
|
361
314
|
const handleLayout = useCallback((direction) => {
|
|
362
315
|
setNodes((currentNodes) => {
|
|
363
|
-
const
|
|
364
|
-
|
|
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
|
-
}, [
|
|
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
|
-
|
|
402
|
-
const
|
|
403
|
-
|
|
404
|
-
|
|
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
|
|
414
|
-
setNodes((prev) =>
|
|
415
|
-
.filter((n) => n.id !== tempId)
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
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
|
|
443
|
-
setNodes((prev) =>
|
|
444
|
-
.filter((n) => n.id !== tempId)
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
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
|
-
}, [
|
|
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,
|
|
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),
|
|
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,
|
|
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),
|
|
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;
|
|
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
|
|
66
|
-
//
|
|
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
|
|
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:
|
|
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
|