@shepai/cli 1.72.9-pr219.08421ee → 1.72.9-pr220.68aacbc
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/create/create-feature.use-case.d.ts +20 -1
- package/dist/packages/core/src/application/use-cases/features/create/create-feature.use-case.d.ts.map +1 -1
- package/dist/packages/core/src/application/use-cases/features/create/create-feature.use-case.js +68 -41
- package/dist/packages/core/src/application/use-cases/features/create/types.d.ts +10 -0
- package/dist/packages/core/src/application/use-cases/features/create/types.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/notifications/notification.service.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/notifications/notification.service.js +2 -4
- package/dist/src/presentation/web/app/actions/create-feature.d.ts.map +1 -1
- package/dist/src/presentation/web/app/actions/create-feature.js +20 -2
- package/dist/src/presentation/web/app/actions/get-feature-metadata.d.ts +10 -0
- package/dist/src/presentation/web/app/actions/get-feature-metadata.d.ts.map +1 -0
- package/dist/src/presentation/web/app/actions/get-feature-metadata.js +14 -0
- package/dist/src/presentation/web/app/api/agent-events/route.d.ts.map +1 -1
- package/dist/src/presentation/web/app/api/agent-events/route.js +16 -17
- package/dist/src/presentation/web/app/build-feature-node-data.d.ts.map +1 -1
- package/dist/src/presentation/web/app/build-feature-node-data.js +2 -26
- package/dist/src/presentation/web/app/build-graph-nodes.d.ts.map +1 -1
- package/dist/src/presentation/web/app/build-graph-nodes.js +2 -26
- package/dist/src/presentation/web/components/common/add-repository-button/add-repository-button.d.ts.map +1 -0
- package/dist/src/presentation/web/components/common/add-repository-button/index.d.ts +2 -0
- package/dist/src/presentation/web/components/common/add-repository-button/index.d.ts.map +1 -0
- package/dist/src/presentation/web/components/common/{add-repository-node → add-repository-button}/index.js +0 -1
- package/dist/src/presentation/web/components/common/add-repository-button/pick-folder.d.ts.map +1 -0
- 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 +13 -18
- package/dist/src/presentation/web/components/common/control-center-drawer/drawer-view.d.ts +2 -0
- package/dist/src/presentation/web/components/common/control-center-drawer/drawer-view.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/control-center-drawer/drawer-view.js +11 -11
- 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 +69 -141
- package/dist/src/presentation/web/components/common/control-center-drawer/index.d.ts +0 -1
- package/dist/src/presentation/web/components/common/control-center-drawer/index.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/control-center-drawer/index.js +0 -1
- package/dist/src/presentation/web/components/common/control-center-drawer/use-artifact-fetch.d.ts +13 -0
- package/dist/src/presentation/web/components/common/control-center-drawer/use-artifact-fetch.d.ts.map +1 -0
- package/dist/src/presentation/web/components/common/control-center-drawer/use-artifact-fetch.js +46 -0
- package/dist/src/presentation/web/components/common/feature-drawer/use-feature-actions.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-drawer/use-feature-actions.js +7 -10
- package/dist/src/presentation/web/components/common/feature-node/derive-feature-state.d.ts +25 -0
- package/dist/src/presentation/web/components/common/feature-node/derive-feature-state.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-node/derive-feature-state.js +45 -2
- package/dist/src/presentation/web/components/common/index.d.ts +2 -3
- package/dist/src/presentation/web/components/common/index.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/index.js +2 -3
- package/dist/src/presentation/web/components/common/repository-node/use-repository-actions.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/repository-node/use-repository-actions.js +14 -39
- package/dist/src/presentation/web/components/features/control-center/control-center-empty-state.js +1 -1
- 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 +31 -14
- package/dist/src/presentation/web/components/features/control-center/control-center.stories.d.ts +0 -11
- package/dist/src/presentation/web/components/features/control-center/control-center.stories.d.ts.map +1 -1
- package/dist/src/presentation/web/components/features/control-center/control-center.stories.js +2 -37
- package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts +8 -0
- 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 +158 -396
- package/dist/src/presentation/web/components/features/features-canvas/features-canvas.d.ts +2 -10
- package/dist/src/presentation/web/components/features/features-canvas/features-canvas.d.ts.map +1 -1
- package/dist/src/presentation/web/components/features/features-canvas/features-canvas.js +11 -54
- package/dist/src/presentation/web/components/features/features-canvas/features-canvas.stories.d.ts.map +1 -1
- package/dist/src/presentation/web/components/features/features-canvas/features-canvas.stories.js +0 -46
- package/dist/src/presentation/web/components/layouts/app-shell/app-shell.js +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 +2 -2
- package/dist/src/presentation/web/components/ui/dialog.d.ts.map +1 -1
- package/dist/src/presentation/web/components/ui/dialog.js +2 -2
- package/dist/src/presentation/web/hooks/agent-events-provider.d.ts.map +1 -1
- package/dist/src/presentation/web/hooks/agent-events-provider.js +3 -2
- package/dist/src/presentation/web/hooks/use-agent-events.d.ts.map +1 -1
- package/dist/src/presentation/web/hooks/use-agent-events.js +16 -5
- package/dist/src/presentation/web/hooks/use-deploy-action.d.ts.map +1 -1
- package/dist/src/presentation/web/hooks/use-deploy-action.js +8 -3
- package/dist/src/presentation/web/hooks/use-graph-state.d.ts +42 -0
- package/dist/src/presentation/web/hooks/use-graph-state.d.ts.map +1 -0
- package/dist/src/presentation/web/hooks/use-graph-state.js +257 -0
- package/dist/src/presentation/web/hooks/use-notifications.d.ts +1 -5
- package/dist/src/presentation/web/hooks/use-notifications.d.ts.map +1 -1
- package/dist/src/presentation/web/hooks/use-notifications.js +1 -26
- package/dist/src/presentation/web/hooks/use-notifications.stories.d.ts.map +1 -1
- package/dist/src/presentation/web/hooks/use-notifications.stories.js +28 -38
- package/dist/src/presentation/web/lib/derive-graph.d.ts +52 -0
- package/dist/src/presentation/web/lib/derive-graph.d.ts.map +1 -0
- package/dist/src/presentation/web/lib/derive-graph.js +126 -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 +0 -1
- 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/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +34 -34
- package/web/.next/server/app/(dashboard)/@drawer/create/page.js +3 -3
- 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 +40 -40
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js +3 -3
- 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 +30 -30
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js +3 -3
- 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 +34 -34
- package/web/.next/server/app/(dashboard)/create/page.js +3 -3
- 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 +40 -40
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js +3 -3
- 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 +30 -30
- package/web/.next/server/app/(dashboard)/page.js +3 -3
- 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 +30 -30
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js +3 -3
- 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.js.nft.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.js.nft.json +1 -1
- package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/tools/page/server-reference-manifest.json +6 -6
- package/web/.next/server/app/tools/page.js.nft.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.js.nft.json +1 -1
- package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
- package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_4d623b8e.js +2 -2
- package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_4d623b8e.js.map +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_816618e2.js +3 -0
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_816618e2.js.map +1 -0
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_ba2e050f.js +3 -0
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_ba2e050f.js.map +1 -0
- package/web/.next/server/chunks/ssr/[root-of-the-server]__17a611f0._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__17a611f0._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__2018e25f._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__2018e25f._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__45dbf93d._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__45dbf93d._.js.map +1 -1
- package/web/.next/server/chunks/ssr/{[root-of-the-server]__cbae9863._.js → [root-of-the-server]__6258349c._.js} +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__6258349c._.js.map +1 -0
- 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]__6cbc1149._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__6cbc1149._.js.map +1 -1
- 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/{standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__e5e09f72._.js → server/chunks/ssr/[root-of-the-server]__94a6d275._.js} +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__94a6d275._.js.map +1 -0
- package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.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/_0e9d6bf7._.js +1 -1
- package/web/.next/server/chunks/ssr/_0e9d6bf7._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_250a63ae._.js +3 -0
- package/web/.next/server/chunks/ssr/_250a63ae._.js.map +1 -0
- package/web/.next/server/chunks/ssr/_61351ed3._.js +1 -1
- package/web/.next/server/chunks/ssr/_61351ed3._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_8400c8bb._.js +1 -1
- package/web/.next/server/chunks/ssr/_8400c8bb._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_9f940437._.js +1 -1
- package/web/.next/server/chunks/ssr/_9f940437._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_ad111e34._.js +6 -0
- package/web/.next/server/chunks/ssr/_ad111e34._.js.map +1 -0
- package/web/.next/{standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_5ea689db._.js → server/chunks/ssr/src_presentation_web_2b91e4ec._.js} +2 -2
- package/web/.next/server/chunks/ssr/{src_presentation_web_7188a290._.js.map → src_presentation_web_2b91e4ec._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/{src_presentation_web_5ea689db._.js → src_presentation_web_5bcc1537._.js} +2 -2
- package/web/.next/server/chunks/ssr/{src_presentation_web_5ea689db._.js.map → src_presentation_web_5bcc1537._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_5955c012.js +3 -0
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_5955c012.js.map +1 -0
- package/web/.next/server/chunks/ssr/src_presentation_web_ca99d62d._.js +3 -0
- package/web/.next/server/chunks/ssr/src_presentation_web_ca99d62d._.js.map +1 -0
- package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.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/chunks/ssr/src_presentation_web_d5b0cb86._.js +8 -0
- package/web/.next/server/chunks/ssr/src_presentation_web_d5b0cb86._.js.map +1 -0
- package/web/.next/server/chunks/ssr/src_presentation_web_e626b63c._.js +8 -0
- package/web/.next/server/chunks/ssr/src_presentation_web_e626b63c._.js.map +1 -0
- 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 +142 -142
- 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/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +34 -34
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/create/page.js +3 -3
- 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 +40 -40
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js +3 -3
- 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 +30 -30
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js +3 -3
- 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 +34 -34
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/create/page.js +3 -3
- 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 +40 -40
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/feature/[featureId]/page.js +3 -3
- 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 +30 -30
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/page.js +3 -3
- 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 +30 -30
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js +3 -3
- 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.js.nft.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.js.nft.json +1 -1
- 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 +6 -6
- package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page.js.nft.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.js.nft.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/403f9_next_dist_esm_build_templates_app-route_4d623b8e.js +2 -2
- 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_816618e2.js +3 -0
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_ba2e050f.js +3 -0
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__17a611f0._.js +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__2018e25f._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__45dbf93d._.js +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/{[root-of-the-server]__cbae9863._.js → [root-of-the-server]__6258349c._.js} +2 -2
- 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]__6cbc1149._.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/{server/chunks/ssr/[root-of-the-server]__e5e09f72._.js → standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__94a6d275._.js} +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.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/_0e9d6bf7._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_250a63ae._.js +3 -0
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_61351ed3._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_8400c8bb._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_9f940437._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_ad111e34._.js +6 -0
- package/web/.next/{server/chunks/ssr/src_presentation_web_7188a290._.js → standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_2b91e4ec._.js} +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/{src_presentation_web_7188a290._.js → src_presentation_web_5bcc1537._.js} +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_5955c012.js +3 -0
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_ca99d62d._.js +3 -0
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.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/chunks/ssr/src_presentation_web_d5b0cb86._.js +8 -0
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_e626b63c._.js +8 -0
- 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 +142 -142
- package/web/.next/standalone/src/presentation/web/CLAUDE.md +77 -33
- package/web/.next/standalone/src/presentation/web/app/actions/create-feature.ts +27 -2
- package/web/.next/standalone/src/presentation/web/app/actions/get-feature-metadata.ts +18 -0
- package/web/.next/standalone/src/presentation/web/app/api/agent-events/route.ts +18 -19
- package/web/.next/standalone/src/presentation/web/app/build-feature-node-data.ts +3 -30
- package/web/.next/standalone/src/presentation/web/app/build-graph-nodes.ts +3 -30
- package/web/.next/standalone/src/presentation/web/components/common/add-repository-button/index.ts +1 -0
- package/web/.next/standalone/src/presentation/web/components/common/control-center-drawer/create-drawer-client.tsx +15 -21
- package/web/.next/standalone/src/presentation/web/components/common/control-center-drawer/drawer-view.ts +12 -13
- package/web/.next/standalone/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.tsx +99 -137
- package/web/.next/standalone/src/presentation/web/components/common/control-center-drawer/index.ts +0 -1
- package/web/.next/standalone/src/presentation/web/components/common/control-center-drawer/use-artifact-fetch.ts +56 -0
- package/web/.next/standalone/src/presentation/web/components/common/feature-drawer/use-feature-actions.ts +6 -7
- package/web/.next/standalone/src/presentation/web/components/common/feature-node/derive-feature-state.ts +63 -2
- package/web/.next/standalone/src/presentation/web/components/common/index.ts +2 -12
- package/web/.next/standalone/src/presentation/web/components/common/repository-node/use-repository-actions.ts +29 -54
- package/web/.next/standalone/src/presentation/web/components/features/control-center/control-center-empty-state.tsx +1 -1
- package/web/.next/standalone/src/presentation/web/components/features/control-center/control-center-inner.tsx +43 -36
- package/web/.next/standalone/src/presentation/web/components/features/control-center/control-center.stories.tsx +2 -41
- package/web/.next/standalone/src/presentation/web/components/features/control-center/use-control-center-state.ts +202 -495
- package/web/.next/standalone/src/presentation/web/components/features/features-canvas/features-canvas.stories.tsx +0 -48
- package/web/.next/standalone/src/presentation/web/components/features/features-canvas/features-canvas.tsx +13 -76
- package/web/.next/standalone/src/presentation/web/components/layouts/app-shell/app-shell.tsx +1 -1
- package/web/.next/standalone/src/presentation/web/components/ui/alert-dialog.tsx +2 -5
- package/web/.next/standalone/src/presentation/web/components/ui/dialog.tsx +2 -5
- package/web/.next/standalone/src/presentation/web/hooks/agent-events-provider.tsx +7 -2
- package/web/.next/standalone/src/presentation/web/hooks/use-agent-events.ts +17 -5
- package/web/.next/standalone/src/presentation/web/hooks/use-deploy-action.ts +11 -3
- package/web/.next/standalone/src/presentation/web/hooks/use-graph-state.ts +376 -0
- package/web/.next/standalone/src/presentation/web/hooks/use-notifications.stories.tsx +2 -30
- package/web/.next/standalone/src/presentation/web/hooks/use-notifications.ts +2 -36
- package/web/.next/standalone/src/presentation/web/lib/derive-graph.ts +178 -0
- package/web/.next/standalone/src/presentation/web/lib/layout-with-dagre.ts +0 -1
- package/web/.next/static/chunks/0ffccd7be73305c8.css +2 -0
- package/web/.next/static/chunks/3e25d4932e1dd406.js +1 -0
- package/web/.next/static/chunks/3f205fd6adbfd20a.js +7 -0
- package/web/.next/static/chunks/68ded4375df20ac5.js +1 -0
- package/web/.next/static/chunks/{1d281f045b623a45.js → 6c13863b031221ea.js} +1 -1
- package/web/.next/static/chunks/8ecedb1a498fb468.js +1 -0
- package/web/.next/static/chunks/95682021c08d3636.js +1 -0
- package/web/.next/static/chunks/ab808dc1cdf66a9c.js +4 -0
- package/web/.next/static/chunks/cb41849bcfaa304e.js +1 -0
- package/web/.next/static/chunks/{cd9099d3eae9dd04.js → ee1a546ece509b1d.js} +1 -1
- package/web/.next/static/chunks/f200e62f7185c8da.js +1 -0
- package/web/.next/static/chunks/fc0f263fd83b43f9.js +1 -0
- package/web/.next/trace +1 -1
- package/web/.next/trace-build +1 -1
- package/dist/src/presentation/web/components/common/add-repository-node/add-repository-button.d.ts.map +0 -1
- package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node-config.d.ts +0 -7
- package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node-config.d.ts.map +0 -1
- package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node-config.js +0 -1
- package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node.d.ts +0 -6
- package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node.d.ts.map +0 -1
- package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node.js +0 -23
- package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node.stories.d.ts +0 -10
- package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node.stories.d.ts.map +0 -1
- package/dist/src/presentation/web/components/common/add-repository-node/add-repository-node.stories.js +0 -33
- package/dist/src/presentation/web/components/common/add-repository-node/index.d.ts +0 -4
- package/dist/src/presentation/web/components/common/add-repository-node/index.d.ts.map +0 -1
- package/dist/src/presentation/web/components/common/add-repository-node/pick-folder.d.ts.map +0 -1
- package/dist/src/presentation/web/components/common/control-center-drawer/control-center-drawer.d.ts +0 -12
- package/dist/src/presentation/web/components/common/control-center-drawer/control-center-drawer.d.ts.map +0 -1
- package/dist/src/presentation/web/components/common/control-center-drawer/control-center-drawer.js +0 -337
- package/dist/src/presentation/web/components/common/control-center-drawer/control-center-drawer.stories.d.ts +0 -33
- package/dist/src/presentation/web/components/common/control-center-drawer/control-center-drawer.stories.d.ts.map +0 -1
- package/dist/src/presentation/web/components/common/control-center-drawer/control-center-drawer.stories.js +0 -214
- package/dist/src/presentation/web/components/common/notification-permission-banner/index.d.ts +0 -2
- package/dist/src/presentation/web/components/common/notification-permission-banner/index.d.ts.map +0 -1
- package/dist/src/presentation/web/components/common/notification-permission-banner/index.js +0 -1
- package/dist/src/presentation/web/components/common/notification-permission-banner/notification-permission-banner.d.ts +0 -2
- package/dist/src/presentation/web/components/common/notification-permission-banner/notification-permission-banner.d.ts.map +0 -1
- package/dist/src/presentation/web/components/common/notification-permission-banner/notification-permission-banner.js +0 -33
- package/dist/src/presentation/web/components/common/notification-permission-banner/notification-permission-banner.stories.d.ts +0 -6
- package/dist/src/presentation/web/components/common/notification-permission-banner/notification-permission-banner.stories.d.ts.map +0 -1
- package/dist/src/presentation/web/components/common/notification-permission-banner/notification-permission-banner.stories.js +0 -24
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_3a02d8a5.js +0 -3
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_3a02d8a5.js.map +0 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_a3044852.js +0 -3
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_a3044852.js.map +0 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__cbae9863._.js.map +0 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__e5e09f72._.js.map +0 -1
- package/web/.next/server/chunks/ssr/_4469c284._.js +0 -6
- package/web/.next/server/chunks/ssr/_4469c284._.js.map +0 -1
- package/web/.next/server/chunks/ssr/_6e08ce21._.js +0 -3
- package/web/.next/server/chunks/ssr/_6e08ce21._.js.map +0 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_0a49d9ee._.js +0 -8
- package/web/.next/server/chunks/ssr/src_presentation_web_0a49d9ee._.js.map +0 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_79b0305a._.js +0 -8
- package/web/.next/server/chunks/ssr/src_presentation_web_79b0305a._.js.map +0 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_cc16bd01.js +0 -3
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_cc16bd01.js.map +0 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_app_(dashboard)_get-graph-data_ts_b018483e._.js +0 -3
- package/web/.next/server/chunks/ssr/src_presentation_web_app_(dashboard)_get-graph-data_ts_b018483e._.js.map +0 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_3a02d8a5.js +0 -3
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_a3044852.js +0 -3
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_4469c284._.js +0 -6
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_6e08ce21._.js +0 -3
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_0a49d9ee._.js +0 -8
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_79b0305a._.js +0 -8
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_cc16bd01.js +0 -3
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_app_(dashboard)_get-graph-data_ts_b018483e._.js +0 -3
- package/web/.next/standalone/src/presentation/web/components/common/add-repository-node/add-repository-node-config.ts +0 -8
- package/web/.next/standalone/src/presentation/web/components/common/add-repository-node/add-repository-node.stories.tsx +0 -70
- package/web/.next/standalone/src/presentation/web/components/common/add-repository-node/add-repository-node.tsx +0 -49
- package/web/.next/standalone/src/presentation/web/components/common/add-repository-node/index.ts +0 -3
- package/web/.next/standalone/src/presentation/web/components/common/control-center-drawer/control-center-drawer.stories.tsx +0 -293
- package/web/.next/standalone/src/presentation/web/components/common/control-center-drawer/control-center-drawer.tsx +0 -745
- package/web/.next/standalone/src/presentation/web/components/common/notification-permission-banner/index.ts +0 -1
- package/web/.next/standalone/src/presentation/web/components/common/notification-permission-banner/notification-permission-banner.stories.tsx +0 -52
- package/web/.next/standalone/src/presentation/web/components/common/notification-permission-banner/notification-permission-banner.tsx +0 -55
- package/web/.next/static/chunks/073b31d7a4974504.js +0 -1
- package/web/.next/static/chunks/0c54314c8c9c9739.js +0 -7
- package/web/.next/static/chunks/0e588a3a4dd7d1c0.js +0 -1
- package/web/.next/static/chunks/2433994d844b7d39.js +0 -1
- package/web/.next/static/chunks/3e041a9008bac888.js +0 -1
- package/web/.next/static/chunks/798624a651d68855.js +0 -4
- package/web/.next/static/chunks/977b9d2b9ef19f7d.js +0 -1
- package/web/.next/static/chunks/9be617fc61b893f2.js +0 -1
- package/web/.next/static/chunks/a18b0561724d11d4.js +0 -1
- package/web/.next/static/chunks/acce8a15d6896dea.css +0 -2
- /package/dist/src/presentation/web/components/common/{add-repository-node → add-repository-button}/add-repository-button.d.ts +0 -0
- /package/dist/src/presentation/web/components/common/{add-repository-node → add-repository-button}/add-repository-button.js +0 -0
- /package/dist/src/presentation/web/components/common/{add-repository-node → add-repository-button}/pick-folder.d.ts +0 -0
- /package/dist/src/presentation/web/components/common/{add-repository-node → add-repository-button}/pick-folder.js +0 -0
- /package/web/.next/standalone/src/presentation/web/components/common/{add-repository-node → add-repository-button}/add-repository-button.tsx +0 -0
- /package/web/.next/standalone/src/presentation/web/components/common/{add-repository-node → add-repository-button}/pick-folder.ts +0 -0
- /package/web/.next/static/{pzwrJGWI__eUhJsxxW2S- → wSVxDvqh-ObIC1qlefqgl}/_buildManifest.js +0 -0
- /package/web/.next/static/{pzwrJGWI__eUhJsxxW2S- → wSVxDvqh-ObIC1qlefqgl}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{pzwrJGWI__eUhJsxxW2S- → wSVxDvqh-ObIC1qlefqgl}/_ssgManifest.js +0 -0
package/dist/src/presentation/web/components/features/control-center/use-control-center-state.js
CHANGED
|
@@ -1,100 +1,55 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import {
|
|
2
|
+
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
|
3
3
|
import { useRouter } from 'next/navigation';
|
|
4
4
|
import { toast } from 'sonner';
|
|
5
|
-
import { applyNodeChanges } from '@xyflow/react';
|
|
6
5
|
import { layoutWithDagre, CANVAS_LAYOUT_DEFAULTS, } from '../../../lib/layout-with-dagre.js';
|
|
7
6
|
import { deleteFeature } from '../../../app/actions/delete-feature.js';
|
|
8
7
|
import { addRepository } from '../../../app/actions/add-repository.js';
|
|
9
8
|
import { deleteRepository } from '../../../app/actions/delete-repository.js';
|
|
10
|
-
import {
|
|
9
|
+
import { getFeatureMetadata } from '../../../app/actions/get-feature-metadata.js';
|
|
11
10
|
import { useAgentEventsContext } from '../../../hooks/agent-events-provider.js';
|
|
12
11
|
import { useSoundAction } from '../../../hooks/use-sound-action.js';
|
|
13
|
-
import { mapEventTypeToState,
|
|
12
|
+
import { mapEventTypeToState, resolveSseEventUpdates, } from '../../common/feature-node/derive-feature-state.js';
|
|
13
|
+
import { useGraphState } from '../../../hooks/use-graph-state.js';
|
|
14
|
+
/** Must match the message string emitted by the SSE route in agent-events/route.ts */
|
|
15
|
+
const METADATA_UPDATED_MESSAGE = 'Feature metadata updated';
|
|
14
16
|
let nextFeatureId = 0;
|
|
15
17
|
export function useControlCenterState(initialNodes, initialEdges) {
|
|
16
18
|
const router = useRouter();
|
|
17
|
-
const [nodes, setNodes] = useState(initialNodes);
|
|
18
|
-
// eslint-disable-next-line react/hook-use-state -- raw setter renamed; public setEdges wrapper keeps edgesRef in sync
|
|
19
|
-
const [edges, setEdgesRaw] = useState(initialEdges);
|
|
20
|
-
const edgesRef = useRef(initialEdges);
|
|
21
|
-
// Wrapper that keeps edgesRef in sync with edges state, allowing
|
|
22
|
-
// createFeatureNode to read current edges without a closure dependency.
|
|
23
|
-
const setEdges = useCallback((update) => {
|
|
24
|
-
setEdgesRaw((prev) => {
|
|
25
|
-
const next = typeof update === 'function' ? update(prev) : update;
|
|
26
|
-
edgesRef.current = next;
|
|
27
|
-
return next;
|
|
28
|
-
});
|
|
29
|
-
}, []);
|
|
30
19
|
const deleteSound = useSoundAction('delete');
|
|
31
20
|
const createSound = useSoundAction('create');
|
|
32
|
-
|
|
33
|
-
|
|
21
|
+
const { nodes, edges, reconcile, updateFeature, addPendingFeature, removeFeature, restoreFeature, addRepository: addRepositoryToMap, removeRepository, replaceRepository, getFeatureRepositoryPath, getRepositoryData, setCallbacks, } = useGraphState(initialNodes, initialEdges);
|
|
22
|
+
// Refs for stable access to latest nodes/edges without callback recreation
|
|
23
|
+
const nodesRef = useRef(nodes);
|
|
24
|
+
const edgesRef = useRef(edges);
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
nodesRef.current = nodes;
|
|
27
|
+
edgesRef.current = edges;
|
|
28
|
+
}, [nodes, edges]);
|
|
29
|
+
// Sync server props into domain Maps when initialNodes/initialEdges change.
|
|
30
|
+
// Keyed by a stable string so we don't re-reconcile on every render.
|
|
31
|
+
const initialNodeKey = useMemo(() => initialNodes
|
|
34
32
|
.map((n) => n.id)
|
|
35
33
|
.sort()
|
|
36
|
-
.join(',');
|
|
37
|
-
const
|
|
38
|
-
.map((e) => e.id)
|
|
39
|
-
.sort()
|
|
40
|
-
.join(',');
|
|
41
|
-
// Track previous feature states so we can detect transitions on server refresh
|
|
42
|
-
const prevFeatureStatesRef = useRef(new Map());
|
|
43
|
-
// Stable key that changes when feature DATA changes (state/lifecycle), not just IDs.
|
|
44
|
-
const initialDataKey = initialNodes
|
|
34
|
+
.join(','), [initialNodes]);
|
|
35
|
+
const initialDataKey = useMemo(() => initialNodes
|
|
45
36
|
.filter((n) => n.type === 'featureNode')
|
|
46
37
|
.map((n) => {
|
|
47
38
|
const d = n.data;
|
|
48
39
|
return `${n.id}:${d.state}:${d.lifecycle}`;
|
|
49
40
|
})
|
|
50
41
|
.sort()
|
|
51
|
-
.join(',');
|
|
52
|
-
|
|
53
|
-
// to avoid infinite re-renders from unstable array references.
|
|
54
|
-
useEffect(() => {
|
|
55
|
-
setNodes((currentNodes) => {
|
|
56
|
-
// Build a lookup of current node positions by ID
|
|
57
|
-
const currentById = new Map(currentNodes.map((n) => [n.id, n]));
|
|
58
|
-
// Identify optimistic "creating" nodes (they have temp IDs not in server data)
|
|
59
|
-
const serverIds = new Set(initialNodes.map((n) => n.id));
|
|
60
|
-
const creatingNodes = currentNodes.filter((n) => n.type === 'featureNode' &&
|
|
61
|
-
n.data.state === 'creating' &&
|
|
62
|
-
!serverIds.has(n.id));
|
|
63
|
-
// Merge server nodes with client positions, tracking if relayout is needed
|
|
64
|
-
let needsRelayout = false;
|
|
65
|
-
const merged = initialNodes.map((serverNode) => {
|
|
66
|
-
// Node already exists on canvas — keep its position, update data
|
|
67
|
-
const existing = currentById.get(serverNode.id);
|
|
68
|
-
if (existing) {
|
|
69
|
-
return { ...serverNode, position: existing.position };
|
|
70
|
-
}
|
|
71
|
-
// New server node — inherit position from an optimistic creating node if available
|
|
72
|
-
if (serverNode.type === 'featureNode' && creatingNodes.length > 0) {
|
|
73
|
-
const donor = creatingNodes.shift();
|
|
74
|
-
return { ...serverNode, position: donor.position };
|
|
75
|
-
}
|
|
76
|
-
// Truly new node with no optimistic counterpart — needs relayout
|
|
77
|
-
needsRelayout = true;
|
|
78
|
-
return serverNode;
|
|
79
|
-
});
|
|
80
|
-
// Also check if non-creating nodes were removed (graph got smaller)
|
|
81
|
-
if (!needsRelayout) {
|
|
82
|
-
needsRelayout = currentNodes.some((cn) => !serverIds.has(cn.id) &&
|
|
83
|
-
!(cn.type === 'featureNode' && cn.data.state === 'creating'));
|
|
84
|
-
}
|
|
85
|
-
if (needsRelayout) {
|
|
86
|
-
const layoutResult = layoutWithDagre(merged, initialEdges, CANVAS_LAYOUT_DEFAULTS);
|
|
87
|
-
setEdges(layoutResult.edges);
|
|
88
|
-
return layoutResult.nodes;
|
|
89
|
-
}
|
|
90
|
-
return merged;
|
|
91
|
-
});
|
|
92
|
-
}, [initialNodeKey, initialNodes, initialEdges, setEdges]);
|
|
42
|
+
.join(','), [initialNodes]);
|
|
43
|
+
const prevReconcileKey = useRef('');
|
|
93
44
|
useEffect(() => {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
45
|
+
const key = `${initialNodeKey}|${initialDataKey}`;
|
|
46
|
+
if (key !== prevReconcileKey.current) {
|
|
47
|
+
prevReconcileKey.current = key;
|
|
48
|
+
reconcile(initialNodes, initialEdges);
|
|
49
|
+
}
|
|
50
|
+
}, [initialNodeKey, initialDataKey, initialNodes, initialEdges, reconcile]);
|
|
51
|
+
// Track previous feature states for fallback notifications
|
|
52
|
+
const prevFeatureStatesRef = useRef(new Map());
|
|
98
53
|
const { events } = useAgentEventsContext();
|
|
99
54
|
useEffect(() => {
|
|
100
55
|
const prevStates = prevFeatureStatesRef.current;
|
|
@@ -104,7 +59,6 @@ export function useControlCenterState(initialNodes, initialEdges) {
|
|
|
104
59
|
const data = node.data;
|
|
105
60
|
const prev = prevStates.get(node.id);
|
|
106
61
|
if (prev !== undefined && prev !== data.state) {
|
|
107
|
-
// Check if SSE already delivered a matching event for this feature
|
|
108
62
|
const sseAlreadyCovered = events.some((e) => e.featureId === data.featureId && mapEventTypeToState(e.eventType) === data.state);
|
|
109
63
|
if (!sseAlreadyCovered) {
|
|
110
64
|
if (data.state === 'done') {
|
|
@@ -115,9 +69,7 @@ export function useControlCenterState(initialNodes, initialEdges) {
|
|
|
115
69
|
description: 'Waiting for your approval',
|
|
116
70
|
action: {
|
|
117
71
|
label: 'Review',
|
|
118
|
-
onClick: () => {
|
|
119
|
-
router.push(`/feature/${data.featureId}`);
|
|
120
|
-
},
|
|
72
|
+
onClick: () => router.push(`/feature/${data.featureId}`),
|
|
121
73
|
},
|
|
122
74
|
});
|
|
123
75
|
}
|
|
@@ -129,315 +81,151 @@ export function useControlCenterState(initialNodes, initialEdges) {
|
|
|
129
81
|
prevStates.set(node.id, data.state);
|
|
130
82
|
}
|
|
131
83
|
}, [initialDataKey, initialNodes, events, router]);
|
|
132
|
-
//
|
|
133
|
-
// Uses `events` array (not `lastEvent`) so that React batching cannot silently
|
|
134
|
-
// drop events when multiple SSE messages arrive in the same tick.
|
|
84
|
+
// SSE effect: only state + lifecycle updates
|
|
135
85
|
const processedEventCountRef = useRef(0);
|
|
136
|
-
// Track known feature IDs so we can detect when SSE reports features not yet on the canvas.
|
|
137
|
-
// Initialised from initialNodes to avoid a race between first render and SSE arrival.
|
|
138
|
-
const knownFeatureIdsRef = useRef(new Set(initialNodes
|
|
139
|
-
.filter((n) => n.type === 'featureNode')
|
|
140
|
-
.map((n) => n.data.featureId)));
|
|
141
|
-
// Keep ref in sync as nodes change (new nodes added, temp nodes replaced, etc.)
|
|
142
|
-
useEffect(() => {
|
|
143
|
-
knownFeatureIdsRef.current = new Set(nodes
|
|
144
|
-
.filter((n) => n.type === 'featureNode')
|
|
145
|
-
.map((n) => n.data.featureId));
|
|
146
|
-
}, [nodes]);
|
|
147
|
-
// Guard against concurrent fetchGraphData calls triggered by unknown-feature detection
|
|
148
|
-
const fetchingGraphRef = useRef(false);
|
|
149
86
|
useEffect(() => {
|
|
87
|
+
// Clamp cursor if events were pruned
|
|
88
|
+
if (processedEventCountRef.current > events.length) {
|
|
89
|
+
processedEventCountRef.current = 0;
|
|
90
|
+
}
|
|
150
91
|
if (events.length <= processedEventCountRef.current)
|
|
151
92
|
return;
|
|
152
93
|
const newEvents = events.slice(processedEventCountRef.current);
|
|
153
94
|
processedEventCountRef.current = events.length;
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
95
|
+
for (const { featureId, state, lifecycle } of resolveSseEventUpdates(newEvents)) {
|
|
96
|
+
if (state !== undefined || lifecycle !== undefined) {
|
|
97
|
+
updateFeature(`feat-${featureId}`, {
|
|
98
|
+
...(state !== undefined && { state }),
|
|
99
|
+
...(lifecycle !== undefined && { lifecycle }),
|
|
100
|
+
});
|
|
160
101
|
}
|
|
161
|
-
const newState = mapEventTypeToState(event.eventType);
|
|
162
|
-
const newLifecycle = mapPhaseNameToLifecycle(event.phaseName);
|
|
163
|
-
// Targeted node update — only clone the matched node
|
|
164
|
-
setNodes((prev) => prev.map((node) => {
|
|
165
|
-
if (node.type !== 'featureNode')
|
|
166
|
-
return node;
|
|
167
|
-
const data = node.data;
|
|
168
|
-
if (data.featureId !== event.featureId)
|
|
169
|
-
return node;
|
|
170
|
-
return {
|
|
171
|
-
...node,
|
|
172
|
-
data: {
|
|
173
|
-
...data,
|
|
174
|
-
state: newState,
|
|
175
|
-
...(newLifecycle !== undefined && { lifecycle: newLifecycle }),
|
|
176
|
-
},
|
|
177
|
-
};
|
|
178
|
-
}));
|
|
179
102
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
103
|
+
}, [events, updateFeature]);
|
|
104
|
+
// Listen for optimistic approval events from the drawer (fires before SSE arrives)
|
|
105
|
+
useEffect(() => {
|
|
106
|
+
const handler = (e) => {
|
|
107
|
+
const { featureId } = e.detail;
|
|
108
|
+
updateFeature(`feat-${featureId}`, { state: 'running' });
|
|
109
|
+
};
|
|
110
|
+
window.addEventListener('shep:feature-approved', handler);
|
|
111
|
+
return () => window.removeEventListener('shep:feature-approved', handler);
|
|
112
|
+
}, [updateFeature]);
|
|
113
|
+
// Separate effect: fetch metadata (name + description) when SSE reports it changed
|
|
114
|
+
const metadataFetchedRef = useRef(new Set());
|
|
115
|
+
const processedMetadataCountRef = useRef(0);
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
// Clamp cursor if events were pruned
|
|
118
|
+
if (processedMetadataCountRef.current > events.length) {
|
|
119
|
+
processedMetadataCountRef.current = 0;
|
|
120
|
+
}
|
|
121
|
+
if (events.length <= processedMetadataCountRef.current)
|
|
122
|
+
return;
|
|
123
|
+
const newEvents = events.slice(processedMetadataCountRef.current);
|
|
124
|
+
processedMetadataCountRef.current = events.length;
|
|
125
|
+
for (const event of newEvents) {
|
|
126
|
+
if (event.message !== METADATA_UPDATED_MESSAGE)
|
|
127
|
+
continue;
|
|
128
|
+
if (metadataFetchedRef.current.has(event.featureId))
|
|
129
|
+
continue;
|
|
130
|
+
metadataFetchedRef.current.add(event.featureId);
|
|
131
|
+
const nodeId = `feat-${event.featureId}`;
|
|
132
|
+
getFeatureMetadata(event.featureId)
|
|
133
|
+
.then((meta) => {
|
|
134
|
+
if (meta) {
|
|
135
|
+
updateFeature(nodeId, { name: meta.name, description: meta.description });
|
|
136
|
+
}
|
|
197
137
|
})
|
|
198
138
|
.catch(() => {
|
|
199
|
-
//
|
|
200
|
-
})
|
|
201
|
-
.finally(() => {
|
|
202
|
-
fetchingGraphRef.current = false;
|
|
139
|
+
// Silent: metadata fetch failure is non-critical
|
|
203
140
|
});
|
|
204
141
|
}
|
|
205
|
-
}, [events,
|
|
206
|
-
//
|
|
207
|
-
//
|
|
208
|
-
//
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
const data = n.data;
|
|
215
|
-
return (data.state === 'running' || data.state === 'action-required' || data.state === 'creating');
|
|
216
|
-
});
|
|
217
|
-
useEffect(() => {
|
|
218
|
-
if (!hasActiveFeature)
|
|
219
|
-
return;
|
|
220
|
-
const interval = setInterval(async () => {
|
|
221
|
-
try {
|
|
222
|
-
const { nodes: serverNodes } = await fetchGraphData();
|
|
223
|
-
setNodes((currentNodes) => {
|
|
224
|
-
const serverById = new Map(serverNodes.map((n) => [n.id, n]));
|
|
225
|
-
let changed = false;
|
|
226
|
-
const merged = currentNodes.map((node) => {
|
|
227
|
-
const serverNode = serverById.get(node.id);
|
|
228
|
-
if (!serverNode || node.type !== 'featureNode')
|
|
229
|
-
return node;
|
|
230
|
-
const currentData = node.data;
|
|
231
|
-
const serverData = serverNode.data;
|
|
232
|
-
// Only update if server data has different state or lifecycle
|
|
233
|
-
if (currentData.state === serverData.state &&
|
|
234
|
-
currentData.lifecycle === serverData.lifecycle) {
|
|
235
|
-
return node;
|
|
236
|
-
}
|
|
237
|
-
changed = true;
|
|
238
|
-
return {
|
|
239
|
-
...node,
|
|
240
|
-
data: {
|
|
241
|
-
...currentData,
|
|
242
|
-
state: serverData.state,
|
|
243
|
-
lifecycle: serverData.lifecycle,
|
|
244
|
-
progress: serverData.progress,
|
|
245
|
-
...(serverData.errorMessage && { errorMessage: serverData.errorMessage }),
|
|
246
|
-
...(serverData.runtime && { runtime: serverData.runtime }),
|
|
247
|
-
...(serverData.agentType && { agentType: serverData.agentType }),
|
|
248
|
-
...(serverData.pr && { pr: serverData.pr }),
|
|
249
|
-
...(serverData.blockedBy && { blockedBy: serverData.blockedBy }),
|
|
250
|
-
},
|
|
251
|
-
};
|
|
252
|
-
});
|
|
253
|
-
// Find new server nodes not yet on the client
|
|
254
|
-
const clientIds = new Set(currentNodes.map((n) => n.id));
|
|
255
|
-
const newServerNodes = serverNodes.filter((n) => !clientIds.has(n.id));
|
|
256
|
-
// Replace optimistic "creating" temp nodes with their real server
|
|
257
|
-
// counterparts. Temp nodes have IDs like "feature-<ts>" while real
|
|
258
|
-
// nodes use "feat-<uuid>". Match by repositoryPath + name.
|
|
259
|
-
if (newServerNodes.length > 0) {
|
|
260
|
-
const creatingIndices = [];
|
|
261
|
-
merged.forEach((n, i) => {
|
|
262
|
-
if (n.type === 'featureNode' && n.data.state === 'creating') {
|
|
263
|
-
creatingIndices.push(i);
|
|
264
|
-
}
|
|
265
|
-
});
|
|
266
|
-
for (const serverNode of newServerNodes) {
|
|
267
|
-
if (serverNode.type !== 'featureNode' || creatingIndices.length === 0)
|
|
268
|
-
continue;
|
|
269
|
-
const idx = creatingIndices.shift();
|
|
270
|
-
// Replace the temp node in-place, keeping its position
|
|
271
|
-
merged[idx] = { ...serverNode, position: merged[idx].position };
|
|
272
|
-
changed = true;
|
|
273
|
-
}
|
|
274
|
-
// Any remaining new server nodes that didn't replace a temp node
|
|
275
|
-
const replacedIds = new Set(merged.map((n) => n.id));
|
|
276
|
-
const trueNewNodes = newServerNodes.filter((n) => !replacedIds.has(n.id));
|
|
277
|
-
if (trueNewNodes.length > 0) {
|
|
278
|
-
changed = true;
|
|
279
|
-
return [...merged, ...trueNewNodes];
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
return changed ? merged : currentNodes;
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
catch {
|
|
286
|
-
// Silently ignore polling errors — SSE is the primary mechanism
|
|
287
|
-
}
|
|
288
|
-
}, 5_000);
|
|
289
|
-
return () => clearInterval(interval);
|
|
290
|
-
}, [hasActiveFeature]);
|
|
291
|
-
const onNodesChange = useCallback((changes) => {
|
|
292
|
-
// Filter out 'replace' changes — in controlled mode these come from
|
|
293
|
-
// React Flow's StoreUpdater syncing the nodes prop back into state.
|
|
294
|
-
// Applying them creates a circular update loop (nodes → enrichedNodes →
|
|
295
|
-
// StoreUpdater → onNodesChange → setNodes → nodes → …) that both causes
|
|
296
|
-
// excessive re-renders and overwrites SSE-driven state changes with stale data.
|
|
297
|
-
const applicable = changes.filter((c) => c.type !== 'replace');
|
|
298
|
-
if (applicable.length > 0) {
|
|
299
|
-
setNodes((ns) => applyNodeChanges(applicable, ns));
|
|
300
|
-
}
|
|
142
|
+
}, [events, updateFeature]);
|
|
143
|
+
// onNodesChange is a no-op: nodes are derived from domain Maps.
|
|
144
|
+
// Since nodesDraggable=false and elementsSelectable=false, only React Flow's
|
|
145
|
+
// internal replace/dimensions changes come through — we ignore them all.
|
|
146
|
+
const onNodesChange = useCallback((_changes) => {
|
|
147
|
+
// Intentional no-op: domain Maps are the source of truth.
|
|
148
|
+
}, []);
|
|
149
|
+
const handleConnect = useCallback((_connection) => {
|
|
150
|
+
// Connections are managed via domain operations, not direct edge manipulation.
|
|
301
151
|
}, []);
|
|
302
|
-
const handleConnect = useCallback((connection) => {
|
|
303
|
-
if (!connection.source || !connection.target)
|
|
304
|
-
return;
|
|
305
|
-
setNodes((currentNodes) => {
|
|
306
|
-
const sourceNode = currentNodes.find((n) => n.id === connection.source);
|
|
307
|
-
if (sourceNode?.type !== 'repositoryNode')
|
|
308
|
-
return currentNodes;
|
|
309
|
-
// Block if target feature already has a repo source
|
|
310
|
-
setEdges((currentEdges) => {
|
|
311
|
-
const targetAlreadyHasRepo = currentEdges.some((e) => {
|
|
312
|
-
const edgeSource = currentNodes.find((n) => n.id === e.source);
|
|
313
|
-
return edgeSource?.type === 'repositoryNode' && e.target === connection.target;
|
|
314
|
-
});
|
|
315
|
-
if (targetAlreadyHasRepo)
|
|
316
|
-
return currentEdges;
|
|
317
|
-
return [
|
|
318
|
-
...currentEdges,
|
|
319
|
-
{
|
|
320
|
-
id: `edge-${connection.source}-${connection.target}`,
|
|
321
|
-
source: connection.source,
|
|
322
|
-
target: connection.target,
|
|
323
|
-
style: { strokeDasharray: '5 5' },
|
|
324
|
-
},
|
|
325
|
-
];
|
|
326
|
-
});
|
|
327
|
-
return currentNodes;
|
|
328
|
-
});
|
|
329
|
-
}, [setEdges]);
|
|
330
152
|
const createFeatureNode = useCallback((sourceNodeId, dataOverride, edgeType) => {
|
|
331
|
-
|
|
153
|
+
// Use real feature ID when available (from server), otherwise temp ID
|
|
154
|
+
const id = dataOverride?.featureId
|
|
155
|
+
? `feat-${dataOverride.featureId}`
|
|
156
|
+
: `feature-${Date.now()}-${nextFeatureId++}`;
|
|
332
157
|
const newFeatureData = {
|
|
333
158
|
name: dataOverride?.name ?? 'New Feature',
|
|
334
159
|
description: dataOverride?.description ?? 'Describe what this feature does',
|
|
335
|
-
featureId: `#${id.slice(-4)}`,
|
|
160
|
+
featureId: dataOverride?.featureId ?? `#${id.slice(-4)}`,
|
|
336
161
|
lifecycle: 'requirements',
|
|
337
162
|
state: dataOverride?.state ?? 'running',
|
|
338
163
|
progress: 0,
|
|
339
164
|
repositoryPath: dataOverride?.repositoryPath ?? '',
|
|
340
165
|
branch: dataOverride?.branch ?? '',
|
|
341
166
|
};
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
id,
|
|
345
|
-
type: 'featureNode',
|
|
346
|
-
position: { x: 0, y: 0 },
|
|
347
|
-
data: newFeatureData,
|
|
348
|
-
};
|
|
349
|
-
const newEdge = sourceNodeId
|
|
350
|
-
? {
|
|
351
|
-
id: edgeType === 'dependencyEdge'
|
|
352
|
-
? `dep-${sourceNodeId}-${id}`
|
|
353
|
-
: `edge-${sourceNodeId}-${id}`,
|
|
354
|
-
source: sourceNodeId,
|
|
355
|
-
target: id,
|
|
356
|
-
...(edgeType ? { type: edgeType } : { style: { strokeDasharray: '5 5' } }),
|
|
357
|
-
}
|
|
358
|
-
: null;
|
|
359
|
-
const allEdges = newEdge
|
|
360
|
-
? [...edgesRef.current.filter((e) => e.id !== newEdge.id), newEdge]
|
|
361
|
-
: edgesRef.current;
|
|
362
|
-
// Insert the new node after the last sibling connected to the same source,
|
|
363
|
-
// so dagre's input-order preservation keeps repo groups together.
|
|
364
|
-
let insertIndex = currentNodes.length;
|
|
365
|
-
if (sourceNodeId) {
|
|
366
|
-
const siblingIds = new Set(allEdges.filter((e) => e.source === sourceNodeId).map((e) => e.target));
|
|
367
|
-
for (let i = currentNodes.length - 1; i >= 0; i--) {
|
|
368
|
-
if (siblingIds.has(currentNodes[i].id)) {
|
|
369
|
-
insertIndex = i + 1;
|
|
370
|
-
break;
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
const allNodes = [
|
|
375
|
-
...currentNodes.slice(0, insertIndex),
|
|
376
|
-
newNode,
|
|
377
|
-
...currentNodes.slice(insertIndex),
|
|
378
|
-
];
|
|
379
|
-
// Run dagre layout on the full graph so the new node is positioned consistently
|
|
380
|
-
const layoutResult = layoutWithDagre(allNodes, allEdges, CANVAS_LAYOUT_DEFAULTS);
|
|
381
|
-
setEdges(layoutResult.edges);
|
|
382
|
-
return layoutResult.nodes;
|
|
383
|
-
});
|
|
167
|
+
const parentNodeId = edgeType === 'dependencyEdge' && sourceNodeId ? sourceNodeId : undefined;
|
|
168
|
+
addPendingFeature(id, newFeatureData, parentNodeId);
|
|
384
169
|
return id;
|
|
385
|
-
}, [
|
|
170
|
+
}, [addPendingFeature]);
|
|
386
171
|
const handleDeleteFeature = useCallback((featureId) => {
|
|
387
172
|
const nodeId = `feat-${featureId}`;
|
|
388
|
-
//
|
|
389
|
-
const
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
return layoutResult.nodes;
|
|
398
|
-
});
|
|
173
|
+
// Find the current entry for rollback
|
|
174
|
+
const prevEntry = nodesRef.current
|
|
175
|
+
.filter((n) => n.id === nodeId)
|
|
176
|
+
.map((n) => ({
|
|
177
|
+
nodeId: n.id,
|
|
178
|
+
data: n.data,
|
|
179
|
+
}))[0];
|
|
180
|
+
// Optimistic removal
|
|
181
|
+
removeFeature(nodeId);
|
|
399
182
|
deleteSound.play();
|
|
400
183
|
toast.success('Feature deleted successfully');
|
|
401
184
|
router.push('/');
|
|
402
|
-
// Persist in background — rollback on failure
|
|
403
185
|
deleteFeature(featureId)
|
|
404
186
|
.then((result) => {
|
|
405
187
|
if (result.error) {
|
|
406
|
-
|
|
407
|
-
|
|
188
|
+
if (prevEntry)
|
|
189
|
+
restoreFeature(nodeId, prevEntry);
|
|
408
190
|
toast.error(result.error);
|
|
409
191
|
}
|
|
410
192
|
})
|
|
411
193
|
.catch(() => {
|
|
412
|
-
|
|
413
|
-
|
|
194
|
+
if (prevEntry)
|
|
195
|
+
restoreFeature(nodeId, prevEntry);
|
|
414
196
|
toast.error('Failed to delete feature');
|
|
415
197
|
});
|
|
416
|
-
}, [
|
|
198
|
+
}, [router, deleteSound, removeFeature, restoreFeature]);
|
|
417
199
|
const handleDeleteRepository = useCallback(async (repositoryId) => {
|
|
418
200
|
const repoNodeId = `repo-${repositoryId}`;
|
|
201
|
+
// Find children of this repo via edges
|
|
202
|
+
const childFeatureIds = new Set(edgesRef.current.filter((e) => e.source === repoNodeId).map((e) => e.target));
|
|
419
203
|
// Snapshot for rollback
|
|
420
|
-
const
|
|
421
|
-
const
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
const
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
}
|
|
204
|
+
const prevRepoData = getRepositoryData(repoNodeId);
|
|
205
|
+
const childSnapshots = new Map();
|
|
206
|
+
for (const childId of childFeatureIds) {
|
|
207
|
+
const childNode = nodesRef.current.find((n) => n.id === childId);
|
|
208
|
+
if (childNode) {
|
|
209
|
+
childSnapshots.set(childId, { nodeId: childId, data: childNode.data });
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
const rollback = () => {
|
|
213
|
+
if (prevRepoData)
|
|
214
|
+
addRepositoryToMap(repoNodeId, prevRepoData);
|
|
215
|
+
for (const [childId, entry] of childSnapshots) {
|
|
216
|
+
restoreFeature(childId, entry);
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
// Optimistic: remove repo + children
|
|
220
|
+
removeRepository(repoNodeId);
|
|
221
|
+
for (const childId of childFeatureIds) {
|
|
222
|
+
removeFeature(childId);
|
|
223
|
+
}
|
|
435
224
|
try {
|
|
436
225
|
const result = await deleteRepository(repositoryId);
|
|
437
226
|
if (!result.success) {
|
|
438
227
|
toast.error(result.error ?? 'Failed to remove repository');
|
|
439
|
-
|
|
440
|
-
setEdges(prevEdges);
|
|
228
|
+
rollback();
|
|
441
229
|
return;
|
|
442
230
|
}
|
|
443
231
|
deleteSound.play();
|
|
@@ -445,82 +233,53 @@ export function useControlCenterState(initialNodes, initialEdges) {
|
|
|
445
233
|
}
|
|
446
234
|
catch {
|
|
447
235
|
toast.error('Failed to remove repository');
|
|
448
|
-
|
|
449
|
-
setEdges(prevEdges);
|
|
236
|
+
rollback();
|
|
450
237
|
}
|
|
451
|
-
}, [
|
|
238
|
+
}, [
|
|
239
|
+
deleteSound,
|
|
240
|
+
removeRepository,
|
|
241
|
+
removeFeature,
|
|
242
|
+
addRepositoryToMap,
|
|
243
|
+
restoreFeature,
|
|
244
|
+
getRepositoryData,
|
|
245
|
+
]);
|
|
452
246
|
const handleLayout = useCallback((direction) => {
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
setEdges(result.edges);
|
|
459
|
-
return result.nodes;
|
|
247
|
+
// Layout is applied via reconcile on next server prop update.
|
|
248
|
+
// For immediate re-layout, we apply dagre and trigger a reconcile-like update.
|
|
249
|
+
const result = layoutWithDagre(nodesRef.current, edgesRef.current, {
|
|
250
|
+
...CANVAS_LAYOUT_DEFAULTS,
|
|
251
|
+
direction,
|
|
460
252
|
});
|
|
461
|
-
|
|
253
|
+
reconcile(result.nodes, result.edges);
|
|
254
|
+
}, [reconcile]);
|
|
462
255
|
const handleAddRepository = useCallback((path) => {
|
|
463
256
|
const tempId = `repo-temp-${Date.now()}`;
|
|
464
257
|
const repoName = path
|
|
465
258
|
.replace(/[\\/]+$/, '')
|
|
466
259
|
.split(/[\\/]/)
|
|
467
260
|
.pop() ?? path;
|
|
468
|
-
|
|
469
|
-
setNodes((currentNodes) => {
|
|
470
|
-
const newNode = {
|
|
471
|
-
id: tempId,
|
|
472
|
-
type: 'repositoryNode',
|
|
473
|
-
position: { x: 0, y: 0 },
|
|
474
|
-
data: { name: repoName, repositoryPath: path, id: tempId },
|
|
475
|
-
};
|
|
476
|
-
const allNodes = [...currentNodes, newNode];
|
|
477
|
-
const layoutResult = layoutWithDagre(allNodes, edgesRef.current, CANVAS_LAYOUT_DEFAULTS);
|
|
478
|
-
setEdges(layoutResult.edges);
|
|
479
|
-
return layoutResult.nodes;
|
|
480
|
-
});
|
|
481
|
-
// Persist via server action
|
|
261
|
+
addRepositoryToMap(tempId, { name: repoName, repositoryPath: path, id: tempId });
|
|
482
262
|
addRepository({ path, name: repoName })
|
|
483
263
|
.then((result) => {
|
|
484
264
|
if (result.error) {
|
|
485
|
-
|
|
486
|
-
setNodes((prev) => {
|
|
487
|
-
const remaining = prev.filter((n) => n.id !== tempId);
|
|
488
|
-
const layoutResult = layoutWithDagre(remaining, edgesRef.current, CANVAS_LAYOUT_DEFAULTS);
|
|
489
|
-
setEdges(layoutResult.edges);
|
|
490
|
-
return layoutResult.nodes;
|
|
491
|
-
});
|
|
265
|
+
removeRepository(tempId);
|
|
492
266
|
toast.error(result.error);
|
|
493
267
|
return;
|
|
494
268
|
}
|
|
495
|
-
// Replace temp ID with real repository ID
|
|
496
269
|
const repo = result.repository;
|
|
497
270
|
const realId = `repo-${repo.id}`;
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
}
|
|
504
|
-
: n));
|
|
505
|
-
setEdges((prev) => prev.map((e) => ({
|
|
506
|
-
...e,
|
|
507
|
-
source: e.source === tempId ? realId : e.source,
|
|
508
|
-
target: e.target === tempId ? realId : e.target,
|
|
509
|
-
id: e.id.replace(tempId, realId),
|
|
510
|
-
})));
|
|
271
|
+
replaceRepository(tempId, realId, {
|
|
272
|
+
name: repo.name,
|
|
273
|
+
repositoryPath: repo.path,
|
|
274
|
+
id: repo.id,
|
|
275
|
+
});
|
|
511
276
|
createSound.play();
|
|
512
277
|
})
|
|
513
278
|
.catch(() => {
|
|
514
|
-
|
|
515
|
-
setNodes((prev) => {
|
|
516
|
-
const remaining = prev.filter((n) => n.id !== tempId);
|
|
517
|
-
const layoutResult = layoutWithDagre(remaining, edgesRef.current, CANVAS_LAYOUT_DEFAULTS);
|
|
518
|
-
setEdges(layoutResult.edges);
|
|
519
|
-
return layoutResult.nodes;
|
|
520
|
-
});
|
|
279
|
+
removeRepository(tempId);
|
|
521
280
|
toast.error('Failed to add repository');
|
|
522
281
|
});
|
|
523
|
-
}, [
|
|
282
|
+
}, [addRepositoryToMap, removeRepository, replaceRepository, createSound]);
|
|
524
283
|
return {
|
|
525
284
|
nodes,
|
|
526
285
|
edges,
|
|
@@ -531,5 +290,8 @@ export function useControlCenterState(initialNodes, initialEdges) {
|
|
|
531
290
|
handleDeleteFeature,
|
|
532
291
|
handleDeleteRepository,
|
|
533
292
|
createFeatureNode,
|
|
293
|
+
getFeatureRepositoryPath,
|
|
294
|
+
getRepositoryData,
|
|
295
|
+
setCallbacks,
|
|
534
296
|
};
|
|
535
297
|
}
|