@shepai/cli 1.72.8 → 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/ports/output/agents/feature-agent-process.interface.d.ts +2 -1
- package/dist/packages/core/src/application/ports/output/agents/feature-agent-process.interface.d.ts.map +1 -1
- package/dist/packages/core/src/application/use-cases/agents/approve-agent-run.use-case.d.ts.map +1 -1
- package/dist/packages/core/src/application/use-cases/agents/approve-agent-run.use-case.js +1 -0
- 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/application/use-cases/features/resume-feature.use-case.d.ts.map +1 -1
- package/dist/packages/core/src/application/use-cases/features/resume-feature.use-case.js +1 -0
- package/dist/packages/core/src/domain/factories/settings-defaults.factory.js +1 -1
- package/dist/packages/core/src/infrastructure/services/agents/common/executors/dev-executor.service.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/agents/common/executors/dev-executor.service.js +88 -13
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-graph.d.ts +1 -1
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-process.service.d.ts +2 -1
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-process.service.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-process.service.js +3 -0
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-worker.d.ts +2 -0
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-worker.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-worker.js +16 -3
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge/merge.node.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge/merge.node.js +5 -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/actions/get-graph-data.d.ts +10 -0
- package/dist/src/presentation/web/app/actions/get-graph-data.d.ts.map +1 -0
- package/dist/src/presentation/web/app/actions/get-graph-data.js +10 -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 +18 -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 +21 -23
- 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/feature-node/derive-feature-state.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-node/derive-feature-state.js +2 -0
- 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/common/repository-node/repository-node.js +1 -1
- package/dist/src/presentation/web/components/common/sidebar-section-header/sidebar-section-header.d.ts +1 -3
- package/dist/src/presentation/web/components/common/sidebar-section-header/sidebar-section-header.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/sidebar-section-header/sidebar-section-header.js +3 -7
- package/dist/src/presentation/web/components/common/sidebar-section-header/sidebar-section-header.stories.d.ts +0 -1
- package/dist/src/presentation/web/components/common/sidebar-section-header/sidebar-section-header.stories.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/sidebar-section-header/sidebar-section-header.stories.js +0 -11
- 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 +145 -256
- 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 +12 -44
- 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/layouts/app-sidebar/app-sidebar.d.ts +1 -3
- package/dist/src/presentation/web/components/layouts/app-sidebar/app-sidebar.d.ts.map +1 -1
- package/dist/src/presentation/web/components/layouts/app-sidebar/app-sidebar.js +2 -2
- package/dist/src/presentation/web/components/ui/scroll-area.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 +1 -2
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/build-manifest.json +2 -2
- package/web/.next/cache/.previewinfo +1 -1
- package/web/.next/cache/.rscinfo +1 -1
- package/web/.next/cache/.tsbuildinfo +1 -1
- package/web/.next/cache/config.json +3 -3
- package/web/.next/fallback-build-manifest.json +2 -2
- package/web/.next/prerender-manifest.json +3 -3
- package/web/.next/required-server-files.js +1 -1
- package/web/.next/required-server-files.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +43 -28
- package/web/.next/server/app/(dashboard)/@drawer/create/page.js +1 -1
- 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 +49 -34
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js +1 -1
- 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 +39 -24
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js +1 -1
- 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 +43 -28
- package/web/.next/server/app/(dashboard)/create/page.js +1 -1
- 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 +49 -34
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js +1 -1
- 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 +39 -24
- package/web/.next/server/app/(dashboard)/page.js +1 -1
- 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 +39 -24
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js +1 -1
- 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]__249c74f6._.js +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.map +1 -1
- 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_398553b0._.js → server/chunks/ssr/src_presentation_web_2b91e4ec._.js} +2 -2
- package/web/.next/server/chunks/ssr/{src_presentation_web_5c87e8e4._.js.map → src_presentation_web_2b91e4ec._.js.map} +1 -1
- package/web/.next/{standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_5c87e8e4._.js → server/chunks/ssr/src_presentation_web_5bcc1537._.js} +2 -2
- package/web/.next/server/chunks/ssr/{src_presentation_web_398553b0._.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 +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_ca99d62d._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_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 +175 -118
- package/web/.next/standalone/src/presentation/web/.next/BUILD_ID +1 -1
- package/web/.next/standalone/src/presentation/web/.next/build-manifest.json +2 -2
- package/web/.next/standalone/src/presentation/web/.next/prerender-manifest.json +3 -3
- package/web/.next/standalone/src/presentation/web/.next/required-server-files.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +43 -28
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/create/page.js +1 -1
- 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 +49 -34
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js +1 -1
- 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 +39 -24
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js +1 -1
- 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 +43 -28
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/create/page.js +1 -1
- 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 +49 -34
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/feature/[featureId]/page.js +1 -1
- 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 +39 -24
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/page.js +1 -1
- 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 +39 -24
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js +1 -1
- 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]__249c74f6._.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/_0e9d6bf7._.js +1 -1
- 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/{server/chunks/ssr/src_presentation_web_398553b0._.js → standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_2b91e4ec._.js} +2 -2
- package/web/.next/{server/chunks/ssr/src_presentation_web_5c87e8e4._.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 +1 -1
- 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 +175 -118
- package/web/.next/standalone/src/presentation/web/CLAUDE.md +191 -0
- 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/actions/get-graph-data.ts +12 -0
- package/web/.next/standalone/src/presentation/web/app/api/agent-events/route.ts +21 -1
- 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 +23 -26
- 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/feature-node/derive-feature-state.ts +2 -0
- package/web/.next/standalone/src/presentation/web/components/common/index.ts +1 -6
- package/web/.next/standalone/src/presentation/web/components/common/repository-node/repository-node.tsx +2 -2
- package/web/.next/standalone/src/presentation/web/components/common/sidebar-section-header/sidebar-section-header.stories.tsx +0 -13
- package/web/.next/standalone/src/presentation/web/components/common/sidebar-section-header/sidebar-section-header.tsx +1 -45
- 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 +178 -325
- 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 +34 -82
- package/web/.next/standalone/src/presentation/web/components/layouts/app-shell/app-shell.tsx +1 -1
- package/web/.next/standalone/src/presentation/web/components/layouts/app-sidebar/app-sidebar.tsx +2 -14
- package/web/.next/standalone/src/presentation/web/components/ui/scroll-area.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 +1 -2
- package/web/.next/standalone/src/presentation/web/server.js +1 -1
- package/web/.next/static/chunks/{1c07b21eb8b32f25.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/{fcafb841e3860687.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/{8682dcb376de62de.js → ae00da647a8354b9.js} +1 -1
- 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_2b7834f6.js +0 -3
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_2b7834f6.js.map +0 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_6923c575.js +0 -3
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_6923c575.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/_47ef589d._.js +0 -6
- package/web/.next/server/chunks/ssr/_47ef589d._.js.map +0 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_1a32dbcb._.js +0 -8
- package/web/.next/server/chunks/ssr/src_presentation_web_1a32dbcb._.js.map +0 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_f4952172.js +0 -3
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_f4952172.js.map +0 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_c1bc6240._.js +0 -8
- package/web/.next/server/chunks/ssr/src_presentation_web_c1bc6240._.js.map +0 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_2b7834f6.js +0 -3
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_6923c575.js +0 -3
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_47ef589d._.js +0 -6
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_1a32dbcb._.js +0 -8
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_f4952172.js +0 -3
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_c1bc6240._.js +0 -8
- 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/3c79bca4d3f18f66.js +0 -1
- package/web/.next/static/chunks/5cd24ca47061a21d.js +0 -4
- package/web/.next/static/chunks/67f2a7a5e8dc358a.css +0 -2
- package/web/.next/static/chunks/71cf72022f08a8c9.js +0 -1
- package/web/.next/static/chunks/991d4490d7a338d8.js +0 -1
- package/web/.next/static/chunks/c71589df79020ecb.js +0 -1
- package/web/.next/static/chunks/ebf172a84c9596ed.js +0 -1
- package/web/.next/static/chunks/fabfa54940973cc7.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/{IKuYPxAuMvc4BtwKHlj30 → JYBKfZ0ROsNn817Id2TJ7}/_buildManifest.js +0 -0
- /package/web/.next/static/{IKuYPxAuMvc4BtwKHlj30 → JYBKfZ0ROsNn817Id2TJ7}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{IKuYPxAuMvc4BtwKHlj30 → JYBKfZ0ROsNn817Id2TJ7}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# Web UI Development Notes
|
|
2
|
+
|
|
3
|
+
## Running the UI
|
|
4
|
+
|
|
5
|
+
There are two distinct ways to run the web UI. **Always test in BOTH modes.**
|
|
6
|
+
|
|
7
|
+
### 1. Dev Mode (local development)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm dev:web
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
- Uses `dev-server.ts` which initializes the DI container and starts Next.js in dev mode
|
|
14
|
+
- Turbopack HMR enabled
|
|
15
|
+
- Runs on port 3000 by default
|
|
16
|
+
|
|
17
|
+
### 2. Production Mode (how users run it)
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
shep ui
|
|
21
|
+
# or
|
|
22
|
+
pnpm dev:cli ui
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
- The CLI bootstraps the DI container (`src/presentation/cli/index.ts`), then starts Next.js via `WebServerService`
|
|
26
|
+
- Runs on port 4050 by default
|
|
27
|
+
- This is how real users experience the UI
|
|
28
|
+
- **IMPORTANT**: Always test real-time features in this mode, not just dev mode
|
|
29
|
+
|
|
30
|
+
### Key Differences Between Modes
|
|
31
|
+
|
|
32
|
+
| Aspect | Dev Mode (`pnpm dev:web`) | Production Mode (`shep ui`) |
|
|
33
|
+
| ------------------ | ------------------------------------------------------- | ------------------------------------------------------- |
|
|
34
|
+
| Entry point | `dev-server.ts` | `cli/index.ts` -> `ui.command.ts` -> `WebServerService` |
|
|
35
|
+
| Port | 3000 | 4050 |
|
|
36
|
+
| DI container | Set on `globalThis.__shepContainer` in dev-server.ts:73 | Set in cli/index.ts:76 |
|
|
37
|
+
| WebSocket upgrades | Forwarded (HMR works) | NOT forwarded in `WebServerService` |
|
|
38
|
+
| Next.js mode | Always dev | dev when run from source, prod when installed |
|
|
39
|
+
|
|
40
|
+
## Graph State Architecture (Domain-Model-Driven)
|
|
41
|
+
|
|
42
|
+
All canvas node/edge state is managed through **domain Maps as the single source of truth**.
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
Server (layout.tsx) Client (useGraphState)
|
|
46
|
+
┌──────────────────┐ ┌─────────────────────────────┐
|
|
47
|
+
│ initialNodes[] │──parseMaps──→ │ Domain Maps (source of truth)│
|
|
48
|
+
│ initialEdges[] │ │ featureMap: Map<id, Entry> │
|
|
49
|
+
└──────────────────┘ │ repoMap: Map<id, Entry> │
|
|
50
|
+
│ pendingMap: Map<id, Entry> │
|
|
51
|
+
SSE Events │ │
|
|
52
|
+
┌──────────────────┐ └──────────┬──────────────────┘
|
|
53
|
+
│ NotificationEvent│──updateFeature──→ │
|
|
54
|
+
└──────────────────┘ │ deriveGraph() + layoutWithDagre()
|
|
55
|
+
▼
|
|
56
|
+
Optimistic UI ┌─────────────────────────────┐
|
|
57
|
+
┌──────────────────┐ │ Derived (read-only) │
|
|
58
|
+
│ shep:feature- │──addPending───→ │ nodes: CanvasNodeType[] │
|
|
59
|
+
│ created event │ │ edges: Edge[] │
|
|
60
|
+
└──────────────────┘ │ (callbacks + layout applied)│
|
|
61
|
+
└─────────────────────────────┘
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Key Files
|
|
65
|
+
|
|
66
|
+
| File | Purpose |
|
|
67
|
+
| ---------------------------------------------------------------- | -------------------------------------------------------------------- |
|
|
68
|
+
| `lib/derive-graph.ts` | Pure function: domain Maps → nodes + edges (with callbacks injected) |
|
|
69
|
+
| `hooks/use-graph-state.ts` | Hook: domain Maps state + reconciliation + derivation + layout |
|
|
70
|
+
| `components/features/control-center/use-control-center-state.ts` | Wraps useGraphState, handles server actions + SSE events |
|
|
71
|
+
| `components/features/control-center/control-center-inner.tsx` | Wires callbacks via `setCallbacks()`, passes nodes/edges to canvas |
|
|
72
|
+
| `components/features/features-canvas/features-canvas.tsx` | Renders React Flow; only adds `selectedFeatureId` highlighting |
|
|
73
|
+
|
|
74
|
+
### Design Principles
|
|
75
|
+
|
|
76
|
+
1. **Domain Maps are the source of truth** — all mutations go through Maps (featureMap, repoMap, pendingMap)
|
|
77
|
+
2. **Edges are derived, never stored** — repo→feature edges from `repositoryPath` matching, dep edges from `parentNodeId`
|
|
78
|
+
3. **Callbacks via ref** — `callbacksRef` + stable `useMemo([])` wrapper prevents circular re-renders
|
|
79
|
+
4. **Layout in derivation** — `layoutWithDagre` runs inside `useMemo` after `deriveGraph()`
|
|
80
|
+
5. **`onNodesChange` is a no-op** — since `nodesDraggable=false`, we ignore all React Flow changes
|
|
81
|
+
|
|
82
|
+
### Mutation Paths
|
|
83
|
+
|
|
84
|
+
| Operation | Flow |
|
|
85
|
+
| ----------------- | ------------------------------------------------------------------------------------------------------- |
|
|
86
|
+
| SSE event | `useAgentEventsContext` → `updateFeature(nodeId, {state, lifecycle})` |
|
|
87
|
+
| Server prop sync | `useEffect` watches `initialNodeKey` → `reconcile(newNodes, newEdges)` |
|
|
88
|
+
| Optimistic create | `shep:feature-created` event → `createFeatureNode()` → `addPendingFeature()` |
|
|
89
|
+
| Delete feature | `handleDeleteFeature()` → `removeFeature()` → server action → `restoreFeature()` on error |
|
|
90
|
+
| Add repository | `handleAddRepository()` → `addRepository(tempId)` → server action → `replaceRepository(tempId, realId)` |
|
|
91
|
+
|
|
92
|
+
### Reconciliation
|
|
93
|
+
|
|
94
|
+
`reconcile(newNodes, newEdges)` merges server data into domain Maps:
|
|
95
|
+
|
|
96
|
+
- Replaces featureMap and repoMap with fresh server data
|
|
97
|
+
- Preserves pending (creating) features in featureMap unless matched by `name + repositoryPath`
|
|
98
|
+
- Cleans pendingMap entries that now have a real counterpart in server data
|
|
99
|
+
|
|
100
|
+
## SSE / Real-Time Update Architecture
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
DB polling (500ms) -> /api/agent-events (SSE route)
|
|
104
|
+
-> Service Worker (public/agent-events-sw.js, single connection)
|
|
105
|
+
-> postMessage to all browser tabs
|
|
106
|
+
-> useAgentEvents hook (hooks/use-agent-events.ts)
|
|
107
|
+
-> AgentEventsProvider context (hooks/agent-events-provider.tsx)
|
|
108
|
+
-> useControlCenterState (SSE effect calls updateFeature)
|
|
109
|
+
-> domain Maps update -> derivation -> React Flow canvas
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### SSE Key Files
|
|
113
|
+
|
|
114
|
+
| File | Purpose |
|
|
115
|
+
| ---------------------------------------------------------------- | ------------------------------------------------------ |
|
|
116
|
+
| `app/api/agent-events/route.ts` | SSE endpoint, polls DB every 500ms, emits delta events |
|
|
117
|
+
| `app/api/agent-events/health/route.ts` | Health check: `/api/agent-events/health` |
|
|
118
|
+
| `public/agent-events-sw.js` | Service Worker: multiplexes single SSE to all tabs |
|
|
119
|
+
| `hooks/use-agent-events.ts` | Client hook: registers with SW, receives events |
|
|
120
|
+
| `hooks/agent-events-provider.tsx` | React context wrapping `useAgentEvents` |
|
|
121
|
+
| `components/features/control-center/use-control-center-state.ts` | SSE effect calls `updateFeature()` on domain Maps |
|
|
122
|
+
| `components/common/feature-node/derive-feature-state.ts` | Maps event types/phases to UI state/lifecycle |
|
|
123
|
+
|
|
124
|
+
### SSE Lifecycle Mappings
|
|
125
|
+
|
|
126
|
+
The SSE route maps `SdlcLifecycle` enum -> agent graph node names -> client `FeatureLifecyclePhase`:
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
SdlcLifecycle.Started -> 'requirements' -> 'requirements'
|
|
130
|
+
SdlcLifecycle.Analyze -> 'analyze' -> 'requirements'
|
|
131
|
+
SdlcLifecycle.Requirements-> 'requirements' -> 'requirements'
|
|
132
|
+
SdlcLifecycle.Research -> 'research' -> 'research'
|
|
133
|
+
SdlcLifecycle.Planning -> 'plan' -> 'implementation'
|
|
134
|
+
SdlcLifecycle.Implementation -> 'implement' -> 'implementation'
|
|
135
|
+
SdlcLifecycle.Review -> 'merge' -> 'review'
|
|
136
|
+
SdlcLifecycle.Maintain -> 'maintain' -> 'maintain'
|
|
137
|
+
SdlcLifecycle.Blocked -> 'blocked' -> 'requirements'
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## React Flow Rendering (CRITICAL)
|
|
141
|
+
|
|
142
|
+
React Flow v12 (`@xyflow/react` ^12.10.0) has specific behaviors that affect real-time updates:
|
|
143
|
+
|
|
144
|
+
- **Controlled mode**: `<ReactFlow nodes={nodes}>` passes nodes as props
|
|
145
|
+
- **Internal Zustand store**: React Flow syncs props to an internal `nodeLookup` Map via `StoreUpdater`
|
|
146
|
+
- **Memoized NodeWrapper**: Each node is wrapped in `memo(NodeWrapper)` which subscribes to store via `useStore(selector, shallow)`
|
|
147
|
+
- **NodeWrapper gets node from store**, NOT from props directly. It does `s.nodeLookup.get(id)` with `shallow` comparison
|
|
148
|
+
- **Single ReactFlowProvider**: There must be exactly ONE `ReactFlowProvider` ancestor. Multiple nested providers create separate stores
|
|
149
|
+
|
|
150
|
+
### Circular Update Loop Prevention
|
|
151
|
+
|
|
152
|
+
With the domain-model-driven architecture, the circular update loop is prevented by design:
|
|
153
|
+
|
|
154
|
+
1. **Callbacks are stable** — injected via `callbacksRef` + stable `useMemo([])` wrapper, so node objects don't change when callbacks change
|
|
155
|
+
2. **`onNodesChange` is a no-op** — domain Maps are the source of truth, React Flow's internal changes are ignored
|
|
156
|
+
3. **`enrichedNodes` only adds `selectedFeatureId`** — no callback injection in FeaturesCanvas
|
|
157
|
+
|
|
158
|
+
**NEVER** make `onNodesChange` apply `replace` changes back to state.
|
|
159
|
+
|
|
160
|
+
## Testing Real-Time Updates
|
|
161
|
+
|
|
162
|
+
1. Start the UI in the mode you want to test
|
|
163
|
+
2. Check SSE health: `curl http://localhost:<port>/api/agent-events/health`
|
|
164
|
+
3. Check SW status in browser: `navigator.serviceWorker.controller?.state` should be "activated"
|
|
165
|
+
4. Trigger state changes: `pnpm dev:cli feat approve <id>` or start a new feature run
|
|
166
|
+
5. Watch both **sidebar** and **canvas nodes** - they should both update
|
|
167
|
+
6. Check browser console for errors
|
|
168
|
+
7. Check server logs for `[SSE] emit:` messages confirming events are sent
|
|
169
|
+
|
|
170
|
+
## Optimistic Feature Creation Flow
|
|
171
|
+
|
|
172
|
+
When a user creates a feature, the UI uses optimistic updates:
|
|
173
|
+
|
|
174
|
+
1. **Create drawer** dispatches `shep:feature-created` → `createFeatureNode()` → `addPendingFeature()` adds temp node to pendingMap
|
|
175
|
+
2. Drawer closes immediately via `router.push('/')`
|
|
176
|
+
3. Server action `createFeature` runs in background → agent starts
|
|
177
|
+
4. SSE events arrive with real feature ID (`feat-<uuid>`) — these update featureMap via `updateFeature()`
|
|
178
|
+
5. **Server prop sync** (rerender with new initialNodes) → `reconcile()` detects matching real feature → removes pending from both featureMap and pendingMap
|
|
179
|
+
|
|
180
|
+
### Key constraints
|
|
181
|
+
|
|
182
|
+
- **Temp node IDs don't match SSE events**: Optimistic nodes use `feature-<ts>` IDs, server uses `feat-<uuid>`. SSE events call `updateFeature('feat-<uuid>', ...)` which doesn't match the temp ID. The reconcile on next server prop update handles the swap.
|
|
183
|
+
- **Create drawer `isSubmitting`**: Must NOT reset until pathname leaves `/create`. Resetting in `.finally()` causes the drawer to flash open because `router.push('/')` is async and pathname hasn't changed yet.
|
|
184
|
+
- **Parallel routes preserve state**: The `CreateDrawerClient` component is NOT unmounted on navigation. `isOpen` is derived from pathname, not mount/unmount lifecycle.
|
|
185
|
+
|
|
186
|
+
## Common Pitfalls
|
|
187
|
+
|
|
188
|
+
- **No events when nothing changes**: SSE only emits deltas. If no feature state changes, no events are sent. This is correct behavior.
|
|
189
|
+
- **Storybook mocks**: New server actions need mocks in `.storybook/mocks/app/actions/` or Storybook build breaks.
|
|
190
|
+
- **`output: 'standalone'`** in next.config.ts: Production builds use standalone mode. Route handlers are bundled differently.
|
|
191
|
+
- **Map insertion order affects dagre layout**: After remove+restore of a feature, the Map insertion order may change, causing slightly different dagre positions. This is expected behavior — don't write tests asserting exact position preservation across remove/restore cycles.
|
|
@@ -72,15 +72,40 @@ export async function createFeature(
|
|
|
72
72
|
|
|
73
73
|
try {
|
|
74
74
|
const createFeatureUseCase = resolve<CreateFeatureUseCase>('CreateFeatureUseCase');
|
|
75
|
-
|
|
75
|
+
|
|
76
|
+
// Phase 1 (fast): create DB record with real UUID — returns immediately
|
|
77
|
+
const { feature, shouldSpawn } = await createFeatureUseCase.createRecord({
|
|
76
78
|
userInput,
|
|
77
79
|
repositoryPath,
|
|
78
80
|
approvalGates: gates,
|
|
79
81
|
push: push ?? false,
|
|
80
82
|
openPr: openPr ?? false,
|
|
81
83
|
...(parentId ? { parentId } : {}),
|
|
84
|
+
name,
|
|
85
|
+
description,
|
|
82
86
|
});
|
|
83
|
-
|
|
87
|
+
|
|
88
|
+
// Phase 2 (background): metadata generation, worktree, spec, agent spawn
|
|
89
|
+
// Fire-and-forget — the UI gets the real feature ID immediately
|
|
90
|
+
createFeatureUseCase
|
|
91
|
+
.initializeAndSpawn(
|
|
92
|
+
feature,
|
|
93
|
+
{
|
|
94
|
+
userInput,
|
|
95
|
+
repositoryPath,
|
|
96
|
+
approvalGates: gates,
|
|
97
|
+
push: push ?? false,
|
|
98
|
+
openPr: openPr ?? false,
|
|
99
|
+
...(parentId ? { parentId } : {}),
|
|
100
|
+
},
|
|
101
|
+
shouldSpawn
|
|
102
|
+
)
|
|
103
|
+
.catch((err: unknown) => {
|
|
104
|
+
// eslint-disable-next-line no-console
|
|
105
|
+
console.error('[createFeature] initializeAndSpawn failed:', err);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
return { feature };
|
|
84
109
|
} catch (error: unknown) {
|
|
85
110
|
const message = error instanceof Error ? error.message : 'Failed to create feature';
|
|
86
111
|
return { error: message };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use server';
|
|
2
|
+
|
|
3
|
+
import { resolve } from '@/lib/server-container';
|
|
4
|
+
import type { IFeatureRepository } from '@shepai/core/application/ports/output/repositories/feature-repository.interface';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Lightweight server action to fetch a single feature's name and description.
|
|
8
|
+
* Used by the SSE effect to update node metadata after AI metadata generation
|
|
9
|
+
* without a full graph reconcile (which can overwrite SSE-driven state).
|
|
10
|
+
*/
|
|
11
|
+
export async function getFeatureMetadata(
|
|
12
|
+
featureId: string
|
|
13
|
+
): Promise<{ name: string; description: string } | null> {
|
|
14
|
+
const featureRepo = resolve<IFeatureRepository>('IFeatureRepository');
|
|
15
|
+
const feature = await featureRepo.findById(featureId);
|
|
16
|
+
if (!feature) return null;
|
|
17
|
+
return { name: feature.name, description: feature.description };
|
|
18
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use server';
|
|
2
|
+
|
|
3
|
+
import { getGraphData } from '@/app/(dashboard)/get-graph-data';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Server action that fetches fresh graph nodes and edges.
|
|
7
|
+
* Used as a lightweight polling fallback for active features,
|
|
8
|
+
* avoiding full router.refresh() page re-renders.
|
|
9
|
+
*/
|
|
10
|
+
export async function fetchGraphData() {
|
|
11
|
+
return getGraphData();
|
|
12
|
+
}
|
|
@@ -44,13 +44,16 @@ interface CachedFeatureState {
|
|
|
44
44
|
* Maps SdlcLifecycle values to agent graph node names so the client
|
|
45
45
|
* can derive the correct FeatureLifecyclePhase via mapPhaseNameToLifecycle().
|
|
46
46
|
*/
|
|
47
|
-
const LIFECYCLE_TO_NODE:
|
|
47
|
+
const LIFECYCLE_TO_NODE: Record<SdlcLifecycle, string> = {
|
|
48
|
+
[SdlcLifecycle.Started]: 'requirements',
|
|
48
49
|
[SdlcLifecycle.Analyze]: 'analyze',
|
|
49
50
|
[SdlcLifecycle.Requirements]: 'requirements',
|
|
50
51
|
[SdlcLifecycle.Research]: 'research',
|
|
51
52
|
[SdlcLifecycle.Planning]: 'plan',
|
|
52
53
|
[SdlcLifecycle.Implementation]: 'implement',
|
|
53
54
|
[SdlcLifecycle.Review]: 'merge',
|
|
55
|
+
[SdlcLifecycle.Maintain]: 'maintain',
|
|
56
|
+
[SdlcLifecycle.Blocked]: 'blocked',
|
|
54
57
|
};
|
|
55
58
|
|
|
56
59
|
const STATUS_TO_EVENT: Partial<
|
|
@@ -188,6 +191,23 @@ export function GET(request: Request): Response {
|
|
|
188
191
|
}
|
|
189
192
|
}
|
|
190
193
|
|
|
194
|
+
// Check for feature name change (AI metadata generation updates name)
|
|
195
|
+
if (prev.featureName !== feature.name) {
|
|
196
|
+
prev.featureName = feature.name;
|
|
197
|
+
const nodeName =
|
|
198
|
+
LIFECYCLE_TO_NODE[feature.lifecycle as SdlcLifecycle] ?? 'requirements';
|
|
199
|
+
emitEvent({
|
|
200
|
+
eventType: NotificationEventType.PhaseCompleted,
|
|
201
|
+
agentRunId: run.id,
|
|
202
|
+
featureId: feature.id,
|
|
203
|
+
featureName: feature.name,
|
|
204
|
+
phaseName: nodeName,
|
|
205
|
+
message: `Feature metadata updated`,
|
|
206
|
+
severity: NotificationSeverity.Info,
|
|
207
|
+
timestamp: new Date().toISOString(),
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
191
211
|
// Check for lifecycle change (agent stays "running" but moves through phases)
|
|
192
212
|
if (prev.lifecycle !== feature.lifecycle) {
|
|
193
213
|
prev.lifecycle = feature.lifecycle;
|
package/web/.next/standalone/src/presentation/web/components/common/add-repository-button/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { AddRepositoryButton } from './add-repository-button';
|
|
@@ -8,7 +8,6 @@ import { FeatureCreateDrawer } from '@/components/common/feature-create-drawer';
|
|
|
8
8
|
import type { FeatureCreatePayload } from '@/components/common/feature-create-drawer';
|
|
9
9
|
import type { ParentFeatureOption } from '@/components/common/feature-create-drawer/feature-create-drawer';
|
|
10
10
|
import type { WorkflowDefaults } from '@/app/actions/get-workflow-defaults';
|
|
11
|
-
import { useSoundAction } from '@/hooks/use-sound-action';
|
|
12
11
|
|
|
13
12
|
export interface CreateDrawerClientProps {
|
|
14
13
|
repositoryPath: string;
|
|
@@ -24,7 +23,6 @@ export function CreateDrawerClient({
|
|
|
24
23
|
workflowDefaults,
|
|
25
24
|
}: CreateDrawerClientProps) {
|
|
26
25
|
const router = useRouter();
|
|
27
|
-
const createSound = useSoundAction('create');
|
|
28
26
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
29
27
|
|
|
30
28
|
// Derive open state from the URL. Next.js parallel routes preserve slot
|
|
@@ -34,15 +32,17 @@ export function CreateDrawerClient({
|
|
|
34
32
|
// When submitting, force the drawer closed immediately — router.push('/')
|
|
35
33
|
// is async and the pathname may not update before the next render.
|
|
36
34
|
const pathname = usePathname();
|
|
37
|
-
const
|
|
35
|
+
const isOnCreateRoute = pathname.startsWith('/create');
|
|
36
|
+
const isOpen = !isSubmitting && isOnCreateRoute;
|
|
38
37
|
|
|
39
|
-
//
|
|
40
|
-
//
|
|
38
|
+
// Reset isSubmitting once the route has actually changed away from /create,
|
|
39
|
+
// so the drawer can reopen on future visits. Without this, isSubmitting
|
|
40
|
+
// would stay true forever since parallel routes preserve component state.
|
|
41
41
|
useEffect(() => {
|
|
42
|
-
if (!
|
|
43
|
-
|
|
42
|
+
if (!isOnCreateRoute && isSubmitting) {
|
|
43
|
+
setIsSubmitting(false);
|
|
44
44
|
}
|
|
45
|
-
}, [
|
|
45
|
+
}, [isOnCreateRoute, isSubmitting]);
|
|
46
46
|
|
|
47
47
|
const onClose = useCallback(() => {
|
|
48
48
|
router.push('/');
|
|
@@ -52,41 +52,38 @@ export function CreateDrawerClient({
|
|
|
52
52
|
(data: FeatureCreatePayload) => {
|
|
53
53
|
setIsSubmitting(true);
|
|
54
54
|
|
|
55
|
-
// Dispatch event for optimistic canvas update before navigating
|
|
56
|
-
window.dispatchEvent(
|
|
57
|
-
new CustomEvent('shep:feature-created', {
|
|
58
|
-
detail: {
|
|
59
|
-
name: data.name,
|
|
60
|
-
description: data.description,
|
|
61
|
-
repositoryPath: data.repositoryPath,
|
|
62
|
-
parentId: data.parentId,
|
|
63
|
-
},
|
|
64
|
-
})
|
|
65
|
-
);
|
|
66
|
-
|
|
67
55
|
// Close the drawer immediately for responsive UI
|
|
68
56
|
router.push('/');
|
|
69
57
|
|
|
70
|
-
//
|
|
58
|
+
// Server action Phase 1 returns fast with real feature ID (DB record created)
|
|
59
|
+
// Phase 2 (metadata, worktree, agent) runs in background on server
|
|
71
60
|
createFeature(data)
|
|
72
61
|
.then((result) => {
|
|
73
62
|
if (result.error) {
|
|
74
63
|
toast.error(result.error);
|
|
75
|
-
// Dispatch rollback event
|
|
76
64
|
window.dispatchEvent(new CustomEvent('shep:feature-create-failed'));
|
|
77
65
|
return;
|
|
78
66
|
}
|
|
79
|
-
|
|
67
|
+
// Dispatch event with real feature ID so control center adds it to featureMap
|
|
68
|
+
window.dispatchEvent(
|
|
69
|
+
new CustomEvent('shep:feature-created', {
|
|
70
|
+
detail: {
|
|
71
|
+
featureId: result.feature!.id,
|
|
72
|
+
name: result.feature!.name,
|
|
73
|
+
description: result.feature!.description,
|
|
74
|
+
repositoryPath: result.feature!.repositoryPath,
|
|
75
|
+
parentId: data.parentId,
|
|
76
|
+
},
|
|
77
|
+
})
|
|
78
|
+
);
|
|
80
79
|
})
|
|
81
80
|
.catch(() => {
|
|
82
81
|
toast.error('Failed to create feature');
|
|
83
82
|
window.dispatchEvent(new CustomEvent('shep:feature-create-failed'));
|
|
84
|
-
})
|
|
85
|
-
.finally(() => {
|
|
86
83
|
setIsSubmitting(false);
|
|
87
84
|
});
|
|
88
85
|
},
|
|
89
|
-
[router
|
|
86
|
+
[router]
|
|
90
87
|
);
|
|
91
88
|
|
|
92
89
|
return (
|
|
@@ -8,7 +8,7 @@ import type {
|
|
|
8
8
|
PrdApprovalPayload,
|
|
9
9
|
QuestionSelectionChange,
|
|
10
10
|
} from '@shepai/core/domain/generated/output';
|
|
11
|
-
import { PrStatus } from '@shepai/core/domain/generated/output';
|
|
11
|
+
import { PrStatus, NotificationEventType } from '@shepai/core/domain/generated/output';
|
|
12
12
|
import { approveFeature } from '@/app/actions/approve-feature';
|
|
13
13
|
import { rejectFeature } from '@/app/actions/reject-feature';
|
|
14
14
|
import { getFeatureArtifact } from '@/app/actions/get-feature-artifact';
|
|
@@ -94,9 +94,22 @@ export function FeatureDrawerClient({ view: initialView }: FeatureDrawerClientPr
|
|
|
94
94
|
const newEvents = events.slice(processedCountRef.current);
|
|
95
95
|
processedCountRef.current = events.length;
|
|
96
96
|
|
|
97
|
+
// PhaseCompleted must NOT set state when WaitingApproval is in the same poll batch.
|
|
98
|
+
// But when it arrives without WaitingApproval (e.g. post-approval), allow it as
|
|
99
|
+
// a fallback for missed AgentStarted events.
|
|
100
|
+
const waitingApprovalInBatch = newEvents.some(
|
|
101
|
+
(e) =>
|
|
102
|
+
e.featureId === featureNode.featureId &&
|
|
103
|
+
e.eventType === NotificationEventType.WaitingApproval
|
|
104
|
+
);
|
|
105
|
+
|
|
97
106
|
for (const event of newEvents) {
|
|
98
107
|
if (event.featureId !== featureNode.featureId) continue;
|
|
99
|
-
|
|
108
|
+
|
|
109
|
+
const newState =
|
|
110
|
+
event.eventType === NotificationEventType.PhaseCompleted && waitingApprovalInBatch
|
|
111
|
+
? undefined
|
|
112
|
+
: mapEventTypeToState(event.eventType);
|
|
100
113
|
const newLifecycle = mapPhaseNameToLifecycle(event.phaseName);
|
|
101
114
|
|
|
102
115
|
// Trigger a server refresh to get the latest drawer view,
|
|
@@ -104,24 +117,26 @@ export function FeatureDrawerClient({ view: initialView }: FeatureDrawerClientPr
|
|
|
104
117
|
// to avoid the refresh interfering with the reject handler's navigation.
|
|
105
118
|
if (!isDirtyRef.current && !isRejectingRef.current) router.refresh();
|
|
106
119
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
120
|
+
if (newState !== undefined || newLifecycle !== undefined) {
|
|
121
|
+
// Optimistically update the node data AND re-derive the view type so the
|
|
122
|
+
// drawer switches immediately (e.g. prd-review → feature when agent resumes)
|
|
123
|
+
// without waiting for the router.refresh() round-trip.
|
|
124
|
+
setView((prev) => {
|
|
125
|
+
if (
|
|
126
|
+
prev.type !== 'feature' &&
|
|
127
|
+
prev.type !== 'prd-review' &&
|
|
128
|
+
prev.type !== 'tech-review' &&
|
|
129
|
+
prev.type !== 'merge-review'
|
|
130
|
+
)
|
|
131
|
+
return prev;
|
|
132
|
+
const updatedNode = {
|
|
119
133
|
...prev.node,
|
|
120
|
-
state: newState,
|
|
134
|
+
...(newState !== undefined && { state: newState }),
|
|
121
135
|
...(newLifecycle !== undefined && { lifecycle: newLifecycle }),
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
|
|
136
|
+
};
|
|
137
|
+
return { ...prev, type: deriveViewType(updatedNode), node: updatedNode };
|
|
138
|
+
});
|
|
139
|
+
}
|
|
125
140
|
}
|
|
126
141
|
}, [events, featureNode, router]);
|
|
127
142
|
|
|
@@ -765,3 +780,14 @@ function DetailRow({ label, value }: { label: string; value: string }) {
|
|
|
765
780
|
</div>
|
|
766
781
|
);
|
|
767
782
|
}
|
|
783
|
+
|
|
784
|
+
/** Mirrors computeDrawerView logic — derives view type from live node data. */
|
|
785
|
+
function deriveViewType(
|
|
786
|
+
node: FeatureNodeData
|
|
787
|
+
): 'feature' | 'prd-review' | 'tech-review' | 'merge-review' {
|
|
788
|
+
if (node.lifecycle === 'requirements' && node.state === 'action-required') return 'prd-review';
|
|
789
|
+
if (node.lifecycle === 'implementation' && node.state === 'action-required') return 'tech-review';
|
|
790
|
+
if (node.lifecycle === 'review' && (node.state === 'action-required' || node.state === 'error'))
|
|
791
|
+
return 'merge-review';
|
|
792
|
+
return 'feature';
|
|
793
|
+
}
|
|
@@ -120,6 +120,8 @@ const phaseNameToLifecycle: Record<string, FeatureLifecyclePhase> = {
|
|
|
120
120
|
plan: 'implementation',
|
|
121
121
|
implement: 'implementation',
|
|
122
122
|
merge: 'review',
|
|
123
|
+
maintain: 'maintain',
|
|
124
|
+
blocked: 'requirements',
|
|
123
125
|
};
|
|
124
126
|
|
|
125
127
|
export function mapPhaseNameToLifecycle(
|
|
@@ -4,11 +4,7 @@ export {
|
|
|
4
4
|
computeDrawerView,
|
|
5
5
|
type DrawerView,
|
|
6
6
|
} from './control-center-drawer';
|
|
7
|
-
export {
|
|
8
|
-
AddRepositoryNode,
|
|
9
|
-
type AddRepositoryNodeData,
|
|
10
|
-
type AddRepositoryNodeType,
|
|
11
|
-
} from './add-repository-node';
|
|
7
|
+
export { AddRepositoryButton } from './add-repository-button';
|
|
12
8
|
export { ElapsedTime, formatElapsed } from './elapsed-time';
|
|
13
9
|
export { EmptyState, type EmptyStateProps } from './empty-state';
|
|
14
10
|
export {
|
|
@@ -35,7 +31,6 @@ export {
|
|
|
35
31
|
export { FeatureStatusBadges, type FeatureStatusBadgesProps } from './feature-status-badges';
|
|
36
32
|
export { FeatureStatusGroup, type FeatureStatusGroupProps } from './feature-status-group';
|
|
37
33
|
export { LoadingSkeleton } from './loading-skeleton';
|
|
38
|
-
export { NotificationPermissionBanner } from './notification-permission-banner';
|
|
39
34
|
export {
|
|
40
35
|
RepositoryNode,
|
|
41
36
|
type RepositoryNodeData,
|
|
@@ -39,7 +39,7 @@ export function RepositoryNode({ data }: { data: RepositoryNodeData; [key: strin
|
|
|
39
39
|
const isDeploymentActive = deployAction.status === 'Booting' || deployAction.status === 'Ready';
|
|
40
40
|
|
|
41
41
|
return (
|
|
42
|
-
<div className=
|
|
42
|
+
<div className={cn('group relative', data.onDelete && data.id && 'pl-10')}>
|
|
43
43
|
{data.showHandles ? (
|
|
44
44
|
<Handle
|
|
45
45
|
type="target"
|
|
@@ -52,7 +52,7 @@ export function RepositoryNode({ data }: { data: RepositoryNodeData; [key: strin
|
|
|
52
52
|
{/* Delete button — visible on hover, positioned to the left */}
|
|
53
53
|
{data.onDelete && data.id ? (
|
|
54
54
|
<>
|
|
55
|
-
<div className="absolute top-1/2
|
|
55
|
+
<div className="absolute top-1/2 left-0 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100">
|
|
56
56
|
<TooltipProvider>
|
|
57
57
|
<Tooltip>
|
|
58
58
|
<TooltipTrigger asChild>
|
|
@@ -28,16 +28,3 @@ export const Default: Story = {
|
|
|
28
28
|
label: 'Features',
|
|
29
29
|
},
|
|
30
30
|
};
|
|
31
|
-
|
|
32
|
-
function noop() {
|
|
33
|
-
// Intentional no-op for Storybook interaction demos
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export const WithActions: Story = {
|
|
38
|
-
args: {
|
|
39
|
-
label: 'Features',
|
|
40
|
-
onFolderClick: noop,
|
|
41
|
-
onMenuClick: noop,
|
|
42
|
-
},
|
|
43
|
-
};
|
|
@@ -1,62 +1,18 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { Folder, GripHorizontal } from 'lucide-react';
|
|
4
3
|
import { SidebarGroup } from '@/components/ui/sidebar';
|
|
5
|
-
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
|
6
|
-
import { useSidebar } from '@/components/ui/sidebar';
|
|
7
4
|
import { cn } from '@/lib/utils';
|
|
8
5
|
|
|
9
6
|
export interface SidebarSectionHeaderProps {
|
|
10
7
|
label: string;
|
|
11
|
-
onFolderClick?: () => void;
|
|
12
|
-
onMenuClick?: () => void;
|
|
13
8
|
className?: string;
|
|
14
9
|
}
|
|
15
10
|
|
|
16
|
-
export function SidebarSectionHeader({
|
|
17
|
-
label,
|
|
18
|
-
onFolderClick,
|
|
19
|
-
onMenuClick,
|
|
20
|
-
className,
|
|
21
|
-
}: SidebarSectionHeaderProps) {
|
|
22
|
-
const { state, isMobile } = useSidebar();
|
|
23
|
-
|
|
11
|
+
export function SidebarSectionHeader({ label, className }: SidebarSectionHeaderProps) {
|
|
24
12
|
return (
|
|
25
13
|
<SidebarGroup data-testid="sidebar-section-header" className={cn('px-2 py-1', className)}>
|
|
26
14
|
<div className="text-sidebar-foreground flex h-7 w-full shrink-0 items-center px-2 text-sm font-medium">
|
|
27
15
|
<span className="min-w-0 flex-1 truncate">{label}</span>
|
|
28
|
-
<span className="text-sidebar-foreground/70 flex shrink-0 items-center gap-0.5">
|
|
29
|
-
<Tooltip>
|
|
30
|
-
<TooltipTrigger asChild>
|
|
31
|
-
<button
|
|
32
|
-
type="button"
|
|
33
|
-
onClick={onFolderClick}
|
|
34
|
-
className="ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground flex size-7 items-center justify-center rounded-md p-0 outline-hidden transition-colors focus-visible:ring-2 [&>svg]:size-4"
|
|
35
|
-
aria-label="Open features folder"
|
|
36
|
-
>
|
|
37
|
-
<Folder className="size-4" />
|
|
38
|
-
</button>
|
|
39
|
-
</TooltipTrigger>
|
|
40
|
-
<TooltipContent side="right" align="center" hidden={state !== 'collapsed' || isMobile}>
|
|
41
|
-
Open features folder
|
|
42
|
-
</TooltipContent>
|
|
43
|
-
</Tooltip>
|
|
44
|
-
<Tooltip>
|
|
45
|
-
<TooltipTrigger asChild>
|
|
46
|
-
<button
|
|
47
|
-
type="button"
|
|
48
|
-
onClick={onMenuClick}
|
|
49
|
-
className="ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground flex size-7 items-center justify-center rounded-md p-0 outline-hidden transition-colors focus-visible:ring-2 [&>svg]:size-4"
|
|
50
|
-
aria-label="Features options"
|
|
51
|
-
>
|
|
52
|
-
<GripHorizontal className="size-4" />
|
|
53
|
-
</button>
|
|
54
|
-
</TooltipTrigger>
|
|
55
|
-
<TooltipContent side="right" align="center" hidden={state !== 'collapsed' || isMobile}>
|
|
56
|
-
Features options
|
|
57
|
-
</TooltipContent>
|
|
58
|
-
</Tooltip>
|
|
59
|
-
</span>
|
|
60
16
|
</div>
|
|
61
17
|
</SidebarGroup>
|
|
62
18
|
);
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { useState, useCallback } from 'react';
|
|
4
4
|
import { Github, Plus, TerminalSquare, Copy, Check, Loader2 } from 'lucide-react';
|
|
5
5
|
import { cn } from '@/lib/utils';
|
|
6
|
-
import { pickFolder } from '@/components/common/add-repository-
|
|
6
|
+
import { pickFolder } from '@/components/common/add-repository-button/pick-folder';
|
|
7
7
|
|
|
8
8
|
export interface ControlCenterEmptyStateProps {
|
|
9
9
|
onRepositorySelect?: (path: string) => void;
|