@shepai/cli 1.72.9-pr218.000ada0 → 1.72.9-pr220.99e5e13
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/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 +15 -0
- 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 -16
- 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 +35 -16
- package/dist/src/presentation/web/components/common/index.d.ts +1 -2
- package/dist/src/presentation/web/components/common/index.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/index.js +1 -2
- 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 +25 -7
- 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 +143 -348
- 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 +10 -42
- 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/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]__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/_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/_9fd79ac5._.js +6 -0
- package/web/.next/server/chunks/ssr/_9fd79ac5._.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]__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/_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/_9fd79ac5._.js +6 -0
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/{src_presentation_web_7188a290._.js → src_presentation_web_2b91e4ec._.js} +2 -2
- package/web/.next/{server/chunks/ssr/src_presentation_web_7188a290._.js → standalone/src/presentation/web/.next/server/chunks/ssr/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 +17 -0
- 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 -19
- package/web/.next/standalone/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.tsx +44 -18
- package/web/.next/standalone/src/presentation/web/components/common/index.ts +1 -6
- 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 +36 -29
- 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 +175 -428
- 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 -59
- package/web/.next/standalone/src/presentation/web/components/layouts/app-shell/app-shell.tsx +1 -1
- 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/{99381b9c479936d5.js → 0b409f42a82665ec.js} +1 -1
- package/web/.next/static/chunks/0e035a073754dde7.js +1 -0
- package/web/.next/static/chunks/109d6898972d7b78.js +1 -0
- package/web/.next/static/chunks/{1249c7c8745b972e.js → 5238c7e71fc81e4d.js} +1 -1
- package/web/.next/static/chunks/6b4899963367ea96.js +4 -0
- package/web/.next/static/chunks/71c9ecdceac1939f.css +2 -0
- package/web/.next/static/chunks/752846e217931a38.js +1 -0
- package/web/.next/static/chunks/9f9e6f5083600784.js +1 -0
- package/web/.next/static/chunks/ae00da647a8354b9.js +1 -0
- package/web/.next/static/chunks/cb41849bcfaa304e.js +1 -0
- package/web/.next/static/chunks/dc23a5773f5cd610.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/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/_6e08ce21._.js +0 -3
- package/web/.next/server/chunks/ssr/_6e08ce21._.js.map +0 -1
- package/web/.next/server/chunks/ssr/_6ef06ca9._.js +0 -6
- package/web/.next/server/chunks/ssr/_6ef06ca9._.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/_6e08ce21._.js +0 -3
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_6ef06ca9._.js +0 -6
- 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/static/chunks/0eeb65b8300fb7cd.js +0 -1
- package/web/.next/static/chunks/1c15ab8668ecf473.js +0 -1
- package/web/.next/static/chunks/2cf89d17ee7717a6.js +0 -4
- package/web/.next/static/chunks/4a8df922364596f6.js +0 -1
- package/web/.next/static/chunks/981b8df9ad045742.js +0 -1
- package/web/.next/static/chunks/9be617fc61b893f2.js +0 -1
- package/web/.next/static/chunks/a676f15c03fa66b9.js +0 -1
- package/web/.next/static/chunks/acce8a15d6896dea.css +0 -2
- package/web/.next/static/chunks/b579fd590a79bf35.js +0 -1
- /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/{nnR12fAsrSagAfeV6xj6D → JYBKfZ0ROsNn817Id2TJ7}/_buildManifest.js +0 -0
- /package/web/.next/static/{nnR12fAsrSagAfeV6xj6D → JYBKfZ0ROsNn817Id2TJ7}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{nnR12fAsrSagAfeV6xj6D → JYBKfZ0ROsNn817Id2TJ7}/_ssgManifest.js +0 -0
package/dist/src/presentation/web/components/features/control-center/use-control-center-state.js
CHANGED
|
@@ -1,46 +1,31 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import {
|
|
2
|
+
import { useCallback, useEffect, 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
12
|
import { mapEventTypeToState, mapPhaseNameToLifecycle, } from '../../common/feature-node/derive-feature-state.js';
|
|
13
|
+
import { NotificationEventType } from '../../../../../../packages/core/src/domain/generated/output.js';
|
|
14
|
+
import { useGraphState } from '../../../hooks/use-graph-state.js';
|
|
15
|
+
/** Must match the message string emitted by the SSE route in agent-events/route.ts */
|
|
16
|
+
const METADATA_UPDATED_MESSAGE = 'Feature metadata updated';
|
|
14
17
|
let nextFeatureId = 0;
|
|
15
18
|
export function useControlCenterState(initialNodes, initialEdges) {
|
|
16
19
|
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
20
|
const deleteSound = useSoundAction('delete');
|
|
31
21
|
const createSound = useSoundAction('create');
|
|
32
|
-
|
|
22
|
+
const { nodes, edges, reconcile, updateFeature, addPendingFeature, removeFeature, restoreFeature, addRepository: addRepositoryToMap, removeRepository, replaceRepository, getFeatureRepositoryPath, getRepositoryData, setCallbacks, } = useGraphState(initialNodes, initialEdges);
|
|
23
|
+
// Sync server props into domain Maps when initialNodes/initialEdges change.
|
|
24
|
+
// Keyed by a stable string so we don't re-reconcile on every render.
|
|
33
25
|
const initialNodeKey = initialNodes
|
|
34
26
|
.map((n) => n.id)
|
|
35
27
|
.sort()
|
|
36
28
|
.join(',');
|
|
37
|
-
const initialEdgeKey = initialEdges
|
|
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
29
|
const initialDataKey = initialNodes
|
|
45
30
|
.filter((n) => n.type === 'featureNode')
|
|
46
31
|
.map((n) => {
|
|
@@ -49,52 +34,16 @@ export function useControlCenterState(initialNodes, initialEdges) {
|
|
|
49
34
|
})
|
|
50
35
|
.sort()
|
|
51
36
|
.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]);
|
|
37
|
+
const prevReconcileKey = useRef('');
|
|
93
38
|
useEffect(() => {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
39
|
+
const key = `${initialNodeKey}|${initialDataKey}`;
|
|
40
|
+
if (key !== prevReconcileKey.current) {
|
|
41
|
+
prevReconcileKey.current = key;
|
|
42
|
+
reconcile(initialNodes, initialEdges);
|
|
43
|
+
}
|
|
44
|
+
}, [initialNodeKey, initialDataKey, initialNodes, initialEdges, reconcile]);
|
|
45
|
+
// Track previous feature states for fallback notifications
|
|
46
|
+
const prevFeatureStatesRef = useRef(new Map());
|
|
98
47
|
const { events } = useAgentEventsContext();
|
|
99
48
|
useEffect(() => {
|
|
100
49
|
const prevStates = prevFeatureStatesRef.current;
|
|
@@ -104,7 +53,6 @@ export function useControlCenterState(initialNodes, initialEdges) {
|
|
|
104
53
|
const data = node.data;
|
|
105
54
|
const prev = prevStates.get(node.id);
|
|
106
55
|
if (prev !== undefined && prev !== data.state) {
|
|
107
|
-
// Check if SSE already delivered a matching event for this feature
|
|
108
56
|
const sseAlreadyCovered = events.some((e) => e.featureId === data.featureId && mapEventTypeToState(e.eventType) === data.state);
|
|
109
57
|
if (!sseAlreadyCovered) {
|
|
110
58
|
if (data.state === 'done') {
|
|
@@ -115,9 +63,7 @@ export function useControlCenterState(initialNodes, initialEdges) {
|
|
|
115
63
|
description: 'Waiting for your approval',
|
|
116
64
|
action: {
|
|
117
65
|
label: 'Review',
|
|
118
|
-
onClick: () => {
|
|
119
|
-
router.push(`/feature/${data.featureId}`);
|
|
120
|
-
},
|
|
66
|
+
onClick: () => router.push(`/feature/${data.featureId}`),
|
|
121
67
|
},
|
|
122
68
|
});
|
|
123
69
|
}
|
|
@@ -129,271 +75,147 @@ export function useControlCenterState(initialNodes, initialEdges) {
|
|
|
129
75
|
prevStates.set(node.id, data.state);
|
|
130
76
|
}
|
|
131
77
|
}, [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.
|
|
78
|
+
// SSE effect: only state + lifecycle updates
|
|
135
79
|
const processedEventCountRef = useRef(0);
|
|
136
80
|
useEffect(() => {
|
|
137
81
|
if (events.length <= processedEventCountRef.current)
|
|
138
82
|
return;
|
|
139
83
|
const newEvents = events.slice(processedEventCountRef.current);
|
|
140
84
|
processedEventCountRef.current = events.length;
|
|
85
|
+
// PhaseCompleted must NOT set state when WaitingApproval is in the same poll batch —
|
|
86
|
+
// they arrive together and PhaseCompleted → 'running' would revert 'action-required'.
|
|
87
|
+
// But when PhaseCompleted arrives WITHOUT WaitingApproval (e.g. after approval), we
|
|
88
|
+
// must allow it to update state as a fallback for any missed AgentStarted event.
|
|
89
|
+
const waitingApprovalFeatures = new Set(newEvents
|
|
90
|
+
.filter((e) => e.eventType === NotificationEventType.WaitingApproval)
|
|
91
|
+
.map((e) => e.featureId));
|
|
141
92
|
for (const event of newEvents) {
|
|
142
|
-
const newState =
|
|
93
|
+
const newState = event.eventType === NotificationEventType.PhaseCompleted &&
|
|
94
|
+
waitingApprovalFeatures.has(event.featureId)
|
|
95
|
+
? undefined
|
|
96
|
+
: mapEventTypeToState(event.eventType);
|
|
143
97
|
const newLifecycle = mapPhaseNameToLifecycle(event.phaseName);
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
if (data.featureId !== event.featureId)
|
|
150
|
-
return node;
|
|
151
|
-
return {
|
|
152
|
-
...node,
|
|
153
|
-
data: {
|
|
154
|
-
...data,
|
|
155
|
-
state: newState,
|
|
156
|
-
...(newLifecycle !== undefined && { lifecycle: newLifecycle }),
|
|
157
|
-
},
|
|
158
|
-
};
|
|
159
|
-
}));
|
|
160
|
-
}
|
|
161
|
-
}, [events]);
|
|
162
|
-
// Lightweight server-action polling fallback: fetches fresh graph data every 5s
|
|
163
|
-
// when any feature is in an active state. This avoids full router.refresh() while
|
|
164
|
-
// ensuring the UI stays in sync even if SSE events are missed.
|
|
165
|
-
//
|
|
166
|
-
// We derive a stable boolean so the interval isn't torn down on every node change.
|
|
167
|
-
const hasActiveFeature = nodes.some((n) => {
|
|
168
|
-
if (n.type !== 'featureNode')
|
|
169
|
-
return false;
|
|
170
|
-
const data = n.data;
|
|
171
|
-
return (data.state === 'running' || data.state === 'action-required' || data.state === 'creating');
|
|
172
|
-
});
|
|
173
|
-
useEffect(() => {
|
|
174
|
-
if (!hasActiveFeature)
|
|
175
|
-
return;
|
|
176
|
-
const interval = setInterval(async () => {
|
|
177
|
-
try {
|
|
178
|
-
const { nodes: serverNodes } = await fetchGraphData();
|
|
179
|
-
setNodes((currentNodes) => {
|
|
180
|
-
const serverById = new Map(serverNodes.map((n) => [n.id, n]));
|
|
181
|
-
let changed = false;
|
|
182
|
-
const merged = currentNodes.map((node) => {
|
|
183
|
-
const serverNode = serverById.get(node.id);
|
|
184
|
-
if (!serverNode || node.type !== 'featureNode')
|
|
185
|
-
return node;
|
|
186
|
-
const currentData = node.data;
|
|
187
|
-
const serverData = serverNode.data;
|
|
188
|
-
// Only update if server data has different state or lifecycle
|
|
189
|
-
if (currentData.state === serverData.state &&
|
|
190
|
-
currentData.lifecycle === serverData.lifecycle) {
|
|
191
|
-
return node;
|
|
192
|
-
}
|
|
193
|
-
changed = true;
|
|
194
|
-
return {
|
|
195
|
-
...node,
|
|
196
|
-
data: {
|
|
197
|
-
...currentData,
|
|
198
|
-
state: serverData.state,
|
|
199
|
-
lifecycle: serverData.lifecycle,
|
|
200
|
-
progress: serverData.progress,
|
|
201
|
-
...(serverData.errorMessage && { errorMessage: serverData.errorMessage }),
|
|
202
|
-
...(serverData.runtime && { runtime: serverData.runtime }),
|
|
203
|
-
...(serverData.agentType && { agentType: serverData.agentType }),
|
|
204
|
-
...(serverData.pr && { pr: serverData.pr }),
|
|
205
|
-
...(serverData.blockedBy && { blockedBy: serverData.blockedBy }),
|
|
206
|
-
},
|
|
207
|
-
};
|
|
208
|
-
});
|
|
209
|
-
// Find new server nodes not yet on the client
|
|
210
|
-
const clientIds = new Set(currentNodes.map((n) => n.id));
|
|
211
|
-
const newServerNodes = serverNodes.filter((n) => !clientIds.has(n.id));
|
|
212
|
-
// Replace optimistic "creating" temp nodes with their real server
|
|
213
|
-
// counterparts. Temp nodes have IDs like "feature-<ts>" while real
|
|
214
|
-
// nodes use "feat-<uuid>". Match by repositoryPath + name.
|
|
215
|
-
if (newServerNodes.length > 0) {
|
|
216
|
-
const creatingIndices = [];
|
|
217
|
-
merged.forEach((n, i) => {
|
|
218
|
-
if (n.type === 'featureNode' && n.data.state === 'creating') {
|
|
219
|
-
creatingIndices.push(i);
|
|
220
|
-
}
|
|
221
|
-
});
|
|
222
|
-
for (const serverNode of newServerNodes) {
|
|
223
|
-
if (serverNode.type !== 'featureNode' || creatingIndices.length === 0)
|
|
224
|
-
continue;
|
|
225
|
-
const idx = creatingIndices.shift();
|
|
226
|
-
// Replace the temp node in-place, keeping its position
|
|
227
|
-
merged[idx] = { ...serverNode, position: merged[idx].position };
|
|
228
|
-
changed = true;
|
|
229
|
-
}
|
|
230
|
-
// Any remaining new server nodes that didn't replace a temp node
|
|
231
|
-
const replacedIds = new Set(merged.map((n) => n.id));
|
|
232
|
-
const trueNewNodes = newServerNodes.filter((n) => !replacedIds.has(n.id));
|
|
233
|
-
if (trueNewNodes.length > 0) {
|
|
234
|
-
changed = true;
|
|
235
|
-
return [...merged, ...trueNewNodes];
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
return changed ? merged : currentNodes;
|
|
98
|
+
const nodeId = `feat-${event.featureId}`;
|
|
99
|
+
if (newState !== undefined || newLifecycle !== undefined) {
|
|
100
|
+
updateFeature(nodeId, {
|
|
101
|
+
...(newState !== undefined && { state: newState }),
|
|
102
|
+
...(newLifecycle !== undefined && { lifecycle: newLifecycle }),
|
|
239
103
|
});
|
|
240
104
|
}
|
|
241
|
-
catch {
|
|
242
|
-
// Silently ignore polling errors — SSE is the primary mechanism
|
|
243
|
-
}
|
|
244
|
-
}, 5_000);
|
|
245
|
-
return () => clearInterval(interval);
|
|
246
|
-
}, [hasActiveFeature]);
|
|
247
|
-
const onNodesChange = useCallback((changes) => {
|
|
248
|
-
// Filter out 'replace' changes — in controlled mode these come from
|
|
249
|
-
// React Flow's StoreUpdater syncing the nodes prop back into state.
|
|
250
|
-
// Applying them creates a circular update loop (nodes → enrichedNodes →
|
|
251
|
-
// StoreUpdater → onNodesChange → setNodes → nodes → …) that both causes
|
|
252
|
-
// excessive re-renders and overwrites SSE-driven state changes with stale data.
|
|
253
|
-
const applicable = changes.filter((c) => c.type !== 'replace');
|
|
254
|
-
if (applicable.length > 0) {
|
|
255
|
-
setNodes((ns) => applyNodeChanges(applicable, ns));
|
|
256
105
|
}
|
|
257
|
-
}, []);
|
|
258
|
-
|
|
259
|
-
|
|
106
|
+
}, [events, updateFeature]);
|
|
107
|
+
// Separate effect: fetch metadata (name + description) when SSE reports it changed
|
|
108
|
+
const metadataFetchedRef = useRef(new Set());
|
|
109
|
+
const processedMetadataCountRef = useRef(0);
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
if (events.length <= processedMetadataCountRef.current)
|
|
260
112
|
return;
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
target: connection.target,
|
|
279
|
-
style: { strokeDasharray: '5 5' },
|
|
280
|
-
},
|
|
281
|
-
];
|
|
113
|
+
const newEvents = events.slice(processedMetadataCountRef.current);
|
|
114
|
+
processedMetadataCountRef.current = events.length;
|
|
115
|
+
for (const event of newEvents) {
|
|
116
|
+
if (event.message !== METADATA_UPDATED_MESSAGE)
|
|
117
|
+
continue;
|
|
118
|
+
if (metadataFetchedRef.current.has(event.featureId))
|
|
119
|
+
continue;
|
|
120
|
+
metadataFetchedRef.current.add(event.featureId);
|
|
121
|
+
const nodeId = `feat-${event.featureId}`;
|
|
122
|
+
getFeatureMetadata(event.featureId)
|
|
123
|
+
.then((meta) => {
|
|
124
|
+
if (meta) {
|
|
125
|
+
updateFeature(nodeId, { name: meta.name, description: meta.description });
|
|
126
|
+
}
|
|
127
|
+
})
|
|
128
|
+
.catch(() => {
|
|
129
|
+
// Silent: metadata fetch failure is non-critical
|
|
282
130
|
});
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
131
|
+
}
|
|
132
|
+
}, [events, updateFeature]);
|
|
133
|
+
// onNodesChange is a no-op: nodes are derived from domain Maps.
|
|
134
|
+
// Since nodesDraggable=false and elementsSelectable=false, only React Flow's
|
|
135
|
+
// internal replace/dimensions changes come through — we ignore them all.
|
|
136
|
+
const onNodesChange = useCallback((_changes) => {
|
|
137
|
+
// Intentional no-op: domain Maps are the source of truth.
|
|
138
|
+
}, []);
|
|
139
|
+
const handleConnect = useCallback((_connection) => {
|
|
140
|
+
// Connections are managed via domain operations, not direct edge manipulation.
|
|
141
|
+
}, []);
|
|
286
142
|
const createFeatureNode = useCallback((sourceNodeId, dataOverride, edgeType) => {
|
|
287
|
-
|
|
143
|
+
// Use real feature ID when available (from server), otherwise temp ID
|
|
144
|
+
const id = dataOverride?.featureId
|
|
145
|
+
? `feat-${dataOverride.featureId}`
|
|
146
|
+
: `feature-${Date.now()}-${nextFeatureId++}`;
|
|
288
147
|
const newFeatureData = {
|
|
289
148
|
name: dataOverride?.name ?? 'New Feature',
|
|
290
149
|
description: dataOverride?.description ?? 'Describe what this feature does',
|
|
291
|
-
featureId: `#${id.slice(-4)}`,
|
|
150
|
+
featureId: dataOverride?.featureId ?? `#${id.slice(-4)}`,
|
|
292
151
|
lifecycle: 'requirements',
|
|
293
152
|
state: dataOverride?.state ?? 'running',
|
|
294
153
|
progress: 0,
|
|
295
154
|
repositoryPath: dataOverride?.repositoryPath ?? '',
|
|
296
155
|
branch: dataOverride?.branch ?? '',
|
|
297
156
|
};
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
id,
|
|
301
|
-
type: 'featureNode',
|
|
302
|
-
position: { x: 0, y: 0 },
|
|
303
|
-
data: newFeatureData,
|
|
304
|
-
};
|
|
305
|
-
const newEdge = sourceNodeId
|
|
306
|
-
? {
|
|
307
|
-
id: edgeType === 'dependencyEdge'
|
|
308
|
-
? `dep-${sourceNodeId}-${id}`
|
|
309
|
-
: `edge-${sourceNodeId}-${id}`,
|
|
310
|
-
source: sourceNodeId,
|
|
311
|
-
target: id,
|
|
312
|
-
...(edgeType ? { type: edgeType } : { style: { strokeDasharray: '5 5' } }),
|
|
313
|
-
}
|
|
314
|
-
: null;
|
|
315
|
-
const allEdges = newEdge
|
|
316
|
-
? [...edgesRef.current.filter((e) => e.id !== newEdge.id), newEdge]
|
|
317
|
-
: edgesRef.current;
|
|
318
|
-
// Insert the new node after the last sibling connected to the same source,
|
|
319
|
-
// so dagre's input-order preservation keeps repo groups together.
|
|
320
|
-
let insertIndex = currentNodes.length;
|
|
321
|
-
if (sourceNodeId) {
|
|
322
|
-
const siblingIds = new Set(allEdges.filter((e) => e.source === sourceNodeId).map((e) => e.target));
|
|
323
|
-
for (let i = currentNodes.length - 1; i >= 0; i--) {
|
|
324
|
-
if (siblingIds.has(currentNodes[i].id)) {
|
|
325
|
-
insertIndex = i + 1;
|
|
326
|
-
break;
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
const allNodes = [
|
|
331
|
-
...currentNodes.slice(0, insertIndex),
|
|
332
|
-
newNode,
|
|
333
|
-
...currentNodes.slice(insertIndex),
|
|
334
|
-
];
|
|
335
|
-
// Run dagre layout on the full graph so the new node is positioned consistently
|
|
336
|
-
const layoutResult = layoutWithDagre(allNodes, allEdges, CANVAS_LAYOUT_DEFAULTS);
|
|
337
|
-
setEdges(layoutResult.edges);
|
|
338
|
-
return layoutResult.nodes;
|
|
339
|
-
});
|
|
157
|
+
const parentNodeId = edgeType === 'dependencyEdge' && sourceNodeId ? sourceNodeId : undefined;
|
|
158
|
+
addPendingFeature(id, newFeatureData, parentNodeId);
|
|
340
159
|
return id;
|
|
341
|
-
}, [
|
|
160
|
+
}, [addPendingFeature]);
|
|
342
161
|
const handleDeleteFeature = useCallback((featureId) => {
|
|
343
162
|
const nodeId = `feat-${featureId}`;
|
|
344
|
-
//
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
return layoutResult.nodes;
|
|
354
|
-
});
|
|
163
|
+
// Find the current entry for rollback
|
|
164
|
+
const prevEntry = nodes
|
|
165
|
+
.filter((n) => n.id === nodeId)
|
|
166
|
+
.map((n) => ({
|
|
167
|
+
nodeId: n.id,
|
|
168
|
+
data: n.data,
|
|
169
|
+
}))[0];
|
|
170
|
+
// Optimistic removal
|
|
171
|
+
removeFeature(nodeId);
|
|
355
172
|
deleteSound.play();
|
|
356
173
|
toast.success('Feature deleted successfully');
|
|
357
174
|
router.push('/');
|
|
358
|
-
// Persist in background — rollback on failure
|
|
359
175
|
deleteFeature(featureId)
|
|
360
176
|
.then((result) => {
|
|
361
177
|
if (result.error) {
|
|
362
|
-
|
|
363
|
-
|
|
178
|
+
if (prevEntry)
|
|
179
|
+
restoreFeature(nodeId, prevEntry);
|
|
364
180
|
toast.error(result.error);
|
|
365
181
|
}
|
|
366
182
|
})
|
|
367
183
|
.catch(() => {
|
|
368
|
-
|
|
369
|
-
|
|
184
|
+
if (prevEntry)
|
|
185
|
+
restoreFeature(nodeId, prevEntry);
|
|
370
186
|
toast.error('Failed to delete feature');
|
|
371
187
|
});
|
|
372
|
-
}, [nodes, router, deleteSound,
|
|
188
|
+
}, [nodes, router, deleteSound, removeFeature, restoreFeature]);
|
|
373
189
|
const handleDeleteRepository = useCallback(async (repositoryId) => {
|
|
374
190
|
const repoNodeId = `repo-${repositoryId}`;
|
|
191
|
+
// Find children of this repo via edges
|
|
192
|
+
const childFeatureIds = new Set(edges.filter((e) => e.source === repoNodeId).map((e) => e.target));
|
|
375
193
|
// Snapshot for rollback
|
|
376
|
-
const
|
|
377
|
-
const
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
const
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
}
|
|
194
|
+
const prevRepoData = getRepositoryData(repoNodeId);
|
|
195
|
+
const childSnapshots = new Map();
|
|
196
|
+
for (const childId of childFeatureIds) {
|
|
197
|
+
const childNode = nodes.find((n) => n.id === childId);
|
|
198
|
+
if (childNode) {
|
|
199
|
+
childSnapshots.set(childId, { nodeId: childId, data: childNode.data });
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
const rollback = () => {
|
|
203
|
+
if (prevRepoData)
|
|
204
|
+
addRepositoryToMap(repoNodeId, prevRepoData);
|
|
205
|
+
for (const [childId, entry] of childSnapshots) {
|
|
206
|
+
restoreFeature(childId, entry);
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
// Optimistic: remove repo + children
|
|
210
|
+
removeRepository(repoNodeId);
|
|
211
|
+
for (const childId of childFeatureIds) {
|
|
212
|
+
removeFeature(childId);
|
|
213
|
+
}
|
|
391
214
|
try {
|
|
392
215
|
const result = await deleteRepository(repositoryId);
|
|
393
216
|
if (!result.success) {
|
|
394
217
|
toast.error(result.error ?? 'Failed to remove repository');
|
|
395
|
-
|
|
396
|
-
setEdges(prevEdges);
|
|
218
|
+
rollback();
|
|
397
219
|
return;
|
|
398
220
|
}
|
|
399
221
|
deleteSound.play();
|
|
@@ -401,82 +223,52 @@ export function useControlCenterState(initialNodes, initialEdges) {
|
|
|
401
223
|
}
|
|
402
224
|
catch {
|
|
403
225
|
toast.error('Failed to remove repository');
|
|
404
|
-
|
|
405
|
-
setEdges(prevEdges);
|
|
226
|
+
rollback();
|
|
406
227
|
}
|
|
407
|
-
}, [
|
|
228
|
+
}, [
|
|
229
|
+
edges,
|
|
230
|
+
nodes,
|
|
231
|
+
deleteSound,
|
|
232
|
+
removeRepository,
|
|
233
|
+
removeFeature,
|
|
234
|
+
addRepositoryToMap,
|
|
235
|
+
restoreFeature,
|
|
236
|
+
getRepositoryData,
|
|
237
|
+
]);
|
|
408
238
|
const handleLayout = useCallback((direction) => {
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
setEdges(result.edges);
|
|
415
|
-
return result.nodes;
|
|
416
|
-
});
|
|
417
|
-
}, [setEdges]);
|
|
239
|
+
// Layout is applied via reconcile on next server prop update.
|
|
240
|
+
// For immediate re-layout, we apply dagre and trigger a reconcile-like update.
|
|
241
|
+
const result = layoutWithDagre(nodes, edges, { ...CANVAS_LAYOUT_DEFAULTS, direction });
|
|
242
|
+
reconcile(result.nodes, result.edges);
|
|
243
|
+
}, [nodes, edges, reconcile]);
|
|
418
244
|
const handleAddRepository = useCallback((path) => {
|
|
419
245
|
const tempId = `repo-temp-${Date.now()}`;
|
|
420
246
|
const repoName = path
|
|
421
247
|
.replace(/[\\/]+$/, '')
|
|
422
248
|
.split(/[\\/]/)
|
|
423
249
|
.pop() ?? path;
|
|
424
|
-
|
|
425
|
-
setNodes((currentNodes) => {
|
|
426
|
-
const newNode = {
|
|
427
|
-
id: tempId,
|
|
428
|
-
type: 'repositoryNode',
|
|
429
|
-
position: { x: 0, y: 0 },
|
|
430
|
-
data: { name: repoName, repositoryPath: path, id: tempId },
|
|
431
|
-
};
|
|
432
|
-
const allNodes = [...currentNodes, newNode];
|
|
433
|
-
const layoutResult = layoutWithDagre(allNodes, edgesRef.current, CANVAS_LAYOUT_DEFAULTS);
|
|
434
|
-
setEdges(layoutResult.edges);
|
|
435
|
-
return layoutResult.nodes;
|
|
436
|
-
});
|
|
437
|
-
// Persist via server action
|
|
250
|
+
addRepositoryToMap(tempId, { name: repoName, repositoryPath: path, id: tempId });
|
|
438
251
|
addRepository({ path, name: repoName })
|
|
439
252
|
.then((result) => {
|
|
440
253
|
if (result.error) {
|
|
441
|
-
|
|
442
|
-
setNodes((prev) => {
|
|
443
|
-
const remaining = prev.filter((n) => n.id !== tempId);
|
|
444
|
-
const layoutResult = layoutWithDagre(remaining, edgesRef.current, CANVAS_LAYOUT_DEFAULTS);
|
|
445
|
-
setEdges(layoutResult.edges);
|
|
446
|
-
return layoutResult.nodes;
|
|
447
|
-
});
|
|
254
|
+
removeRepository(tempId);
|
|
448
255
|
toast.error(result.error);
|
|
449
256
|
return;
|
|
450
257
|
}
|
|
451
|
-
// Replace temp ID with real repository ID
|
|
452
258
|
const repo = result.repository;
|
|
453
259
|
const realId = `repo-${repo.id}`;
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
}
|
|
460
|
-
: n));
|
|
461
|
-
setEdges((prev) => prev.map((e) => ({
|
|
462
|
-
...e,
|
|
463
|
-
source: e.source === tempId ? realId : e.source,
|
|
464
|
-
target: e.target === tempId ? realId : e.target,
|
|
465
|
-
id: e.id.replace(tempId, realId),
|
|
466
|
-
})));
|
|
260
|
+
replaceRepository(tempId, realId, {
|
|
261
|
+
name: repo.name,
|
|
262
|
+
repositoryPath: repo.path,
|
|
263
|
+
id: repo.id,
|
|
264
|
+
});
|
|
467
265
|
createSound.play();
|
|
468
266
|
})
|
|
469
267
|
.catch(() => {
|
|
470
|
-
|
|
471
|
-
setNodes((prev) => {
|
|
472
|
-
const remaining = prev.filter((n) => n.id !== tempId);
|
|
473
|
-
const layoutResult = layoutWithDagre(remaining, edgesRef.current, CANVAS_LAYOUT_DEFAULTS);
|
|
474
|
-
setEdges(layoutResult.edges);
|
|
475
|
-
return layoutResult.nodes;
|
|
476
|
-
});
|
|
268
|
+
removeRepository(tempId);
|
|
477
269
|
toast.error('Failed to add repository');
|
|
478
270
|
});
|
|
479
|
-
}, [
|
|
271
|
+
}, [addRepositoryToMap, removeRepository, replaceRepository, createSound]);
|
|
480
272
|
return {
|
|
481
273
|
nodes,
|
|
482
274
|
edges,
|
|
@@ -487,5 +279,8 @@ export function useControlCenterState(initialNodes, initialEdges) {
|
|
|
487
279
|
handleDeleteFeature,
|
|
488
280
|
handleDeleteRepository,
|
|
489
281
|
createFeatureNode,
|
|
282
|
+
getFeatureRepositoryPath,
|
|
283
|
+
getRepositoryData,
|
|
284
|
+
setCallbacks,
|
|
490
285
|
};
|
|
491
286
|
}
|
|
@@ -1,27 +1,19 @@
|
|
|
1
1
|
import type { Connection, Edge, NodeChange } from '@xyflow/react';
|
|
2
2
|
import type { FeatureNodeType } from '../../common/feature-node/index.js';
|
|
3
3
|
import type { RepositoryNodeType } from '../../common/repository-node/index.js';
|
|
4
|
-
|
|
5
|
-
export type CanvasNodeType = FeatureNodeType | RepositoryNodeType | AddRepositoryNodeType;
|
|
4
|
+
export type CanvasNodeType = FeatureNodeType | RepositoryNodeType;
|
|
6
5
|
export interface FeaturesCanvasProps {
|
|
7
6
|
nodes: CanvasNodeType[];
|
|
8
7
|
edges: Edge[];
|
|
9
8
|
selectedFeatureId?: string | null;
|
|
10
9
|
onNodesChange?: (changes: NodeChange<CanvasNodeType>[]) => void;
|
|
11
10
|
onAddFeature?: () => void;
|
|
12
|
-
onNodeAction?: (nodeId: string) => void;
|
|
13
|
-
onNodeSettings?: (nodeId: string) => void;
|
|
14
11
|
onNodeClick?: (event: React.MouseEvent, node: CanvasNodeType) => void;
|
|
15
12
|
onPaneClick?: (event: React.MouseEvent) => void;
|
|
16
|
-
onRepositoryAdd?: (repoNodeId: string) => void;
|
|
17
|
-
onRepositoryClick?: (nodeId: string) => void;
|
|
18
|
-
onRepositoryDelete?: (repositoryId: string) => void;
|
|
19
|
-
onFeatureDelete?: (featureId: string) => void;
|
|
20
13
|
onConnect?: (connection: Connection) => void;
|
|
21
|
-
onRepositorySelect?: (path: string) => void;
|
|
22
14
|
onCanvasDrag?: () => void;
|
|
23
15
|
toolbar?: React.ReactNode;
|
|
24
16
|
emptyState?: React.ReactNode;
|
|
25
17
|
}
|
|
26
|
-
export declare function FeaturesCanvas({ nodes, edges, selectedFeatureId, onNodesChange, onAddFeature,
|
|
18
|
+
export declare function FeaturesCanvas({ nodes, edges, selectedFeatureId, onNodesChange, onAddFeature, onConnect, onNodeClick, onPaneClick, onCanvasDrag, toolbar, emptyState, }: FeaturesCanvasProps): import("react/jsx-runtime").JSX.Element;
|
|
27
19
|
//# sourceMappingURL=features-canvas.d.ts.map
|