@shepai/cli 1.73.1 → 1.74.0-pr226.8985ba7
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/src/presentation/web/app/actions/create-feature.d.ts +1 -2
- package/dist/src/presentation/web/app/actions/create-feature.d.ts.map +1 -1
- package/dist/src/presentation/web/app/actions/create-feature.js +6 -10
- package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.d.ts +1 -2
- package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.js +4 -9
- package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.d.ts +7 -8
- package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.js +12 -15
- 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 +6 -1
- package/dist/src/presentation/web/components/features/features-canvas/dependency-edge.stories.d.ts.map +1 -1
- package/dist/src/presentation/web/components/features/features-canvas/dependency-edge.stories.js +1 -1
- package/dist/src/presentation/web/components/features/features-canvas/features-canvas.d.ts +5 -2
- 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 +14 -5
- package/dist/src/presentation/web/components/features/features-canvas/features-canvas.stories.d.ts +2 -0
- 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 +16 -0
- package/dist/src/presentation/web/hooks/use-viewport-persistence.d.ts +11 -0
- package/dist/src/presentation/web/hooks/use-viewport-persistence.d.ts.map +1 -0
- package/dist/src/presentation/web/hooks/use-viewport-persistence.js +64 -0
- 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 +16 -16
- 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 +19 -19
- 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 +14 -14
- 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 +16 -16
- 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 +19 -19
- 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 +14 -14
- 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 +14 -14
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/_global-error.html +2 -2
- package/web/.next/server/app/_global-error.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/skills/page/server-reference-manifest.json +6 -6
- package/web/.next/server/app/skills/page.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_client-reference-manifest.js +1 -1
- package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
- package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js.map +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_da7eef33.js +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_da7eef33.js.map +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_0a9251aa.js +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_0a9251aa.js.map +1 -1
- 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]__6258349c._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__6258349c._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__6cbc1149._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__94a6d275._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__94a6d275._.js.map +1 -1
- 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/{_5024c80d._.js → _165a711c._.js} +3 -3
- package/web/.next/server/chunks/ssr/{_5024c80d._.js.map → _165a711c._.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/src_presentation_web_163038ba._.js +2 -4
- package/web/.next/server/chunks/ssr/src_presentation_web_163038ba._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_30d7eeda._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_30d7eeda._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_459cb687._.js +2 -4
- package/web/.next/server/chunks/ssr/src_presentation_web_459cb687._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_565a14f8._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_565a14f8._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_021a05f2.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_021a05f2.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js.map +1 -1
- package/web/.next/server/pages/500.html +2 -2
- package/web/.next/server/server-reference-manifest.js +1 -1
- package/web/.next/server/server-reference-manifest.json +22 -22
- 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 +16 -16
- 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 +19 -19
- 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 +14 -14
- 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 +16 -16
- 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 +19 -19
- 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 +14 -14
- 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 +14 -14
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.html +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page/server-reference-manifest.json +6 -6
- package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page.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_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_da7eef33.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_0a9251aa.js +1 -1
- 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]__6258349c._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__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/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__94a6d275._.js +1 -1
- 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/{_5024c80d._.js → _165a711c._.js} +3 -3
- 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/src_presentation_web_163038ba._.js +2 -4
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_30d7eeda._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_459cb687._.js +2 -4
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_565a14f8._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_021a05f2.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/pages/500.html +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.json +22 -22
- package/web/.next/standalone/src/presentation/web/app/actions/create-feature.ts +7 -18
- package/web/.next/standalone/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.tsx +16 -15
- package/web/.next/standalone/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.tsx +12 -31
- package/web/.next/standalone/src/presentation/web/components/features/control-center/control-center-inner.tsx +18 -1
- package/web/.next/standalone/src/presentation/web/components/features/features-canvas/dependency-edge.stories.tsx +1 -0
- package/web/.next/standalone/src/presentation/web/components/features/features-canvas/features-canvas.stories.tsx +18 -0
- package/web/.next/standalone/src/presentation/web/components/features/features-canvas/features-canvas.tsx +33 -6
- package/web/.next/standalone/src/presentation/web/hooks/use-viewport-persistence.ts +79 -0
- package/web/.next/standalone/src/presentation/web/server.js +1 -1
- package/web/.next/static/chunks/{0acf10ef003e9399.js → 0efb4eebe8e287d6.js} +1 -1
- package/web/.next/static/chunks/{59a74c7dda3d6bb1.js → 32e5da584b2ee731.js} +1 -1
- package/web/.next/static/chunks/{66c3cc54f61b0683.js → 571e15176854c4e5.js} +1 -1
- package/web/.next/static/chunks/{a36a9b78fa84bfd9.js → 5b6ecf78282b255f.js} +1 -1
- package/web/.next/static/chunks/{c6904f5b67ddbcc0.js → 64735b1b52b1da19.js} +2 -2
- package/web/.next/static/chunks/{403ed451ae4f0fce.js → 6a1a0ba49a68bbe1.js} +1 -1
- package/web/.next/static/chunks/{fa3f61d528918657.js → 8be2936957914955.js} +1 -1
- package/web/.next/static/chunks/a2d555fed8db6a7b.js +1 -0
- package/web/.next/static/chunks/{46b663a9110c6602.js → dbda0b3e20712ad2.js} +1 -1
- package/web/.next/trace +1 -1
- package/web/.next/trace-build +1 -1
- package/web/.next/static/chunks/4093f165b5f2cc3e.js +0 -1
- /package/web/.next/static/{aNoBruNQb2aXRBW0KalUh → 9zQ15Z-LG-2oWOuc-wpQh}/_buildManifest.js +0 -0
- /package/web/.next/static/{aNoBruNQb2aXRBW0KalUh → 9zQ15Z-LG-2oWOuc-wpQh}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{aNoBruNQb2aXRBW0KalUh → 9zQ15Z-LG-2oWOuc-wpQh}/_ssgManifest.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-feature.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/app/actions/create-feature.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sCAAsC,CAAC;AAEpE,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAQD,UAAU,kBAAkB;IAC1B,
|
|
1
|
+
{"version":3,"file":"create-feature.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/app/actions/create-feature.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sCAAsC,CAAC;AAEpE,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAQD,UAAU,kBAAkB;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,aAAa,CAAC,EAAE;QACd,QAAQ,EAAE,OAAO,CAAC;QAClB,SAAS,EAAE,OAAO,CAAC;QACnB,UAAU,CAAC,EAAE,OAAO,CAAC;KACtB,CAAC;IACF,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAaD,wBAAsB,aAAa,CACjC,KAAK,EAAE,kBAAkB,GACxB,OAAO,CAAC;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAyDhD"}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
'use server';
|
|
2
2
|
import { resolve } from '../../lib/server-container.js';
|
|
3
|
-
function composeUserInput(
|
|
4
|
-
let userInput =
|
|
5
|
-
if (description) {
|
|
6
|
-
userInput += `\n\n${description}`;
|
|
7
|
-
}
|
|
3
|
+
function composeUserInput(description, attachments) {
|
|
4
|
+
let userInput = description;
|
|
8
5
|
if (attachments && attachments.length > 0) {
|
|
9
6
|
const paths = attachments.map((a) => `- ${a.path}`).join('\n');
|
|
10
7
|
userInput += `\n\nAttached files:\n${paths}`;
|
|
@@ -12,14 +9,14 @@ function composeUserInput(name, description, attachments) {
|
|
|
12
9
|
return userInput;
|
|
13
10
|
}
|
|
14
11
|
export async function createFeature(input) {
|
|
15
|
-
const {
|
|
16
|
-
if (!
|
|
17
|
-
return { error: '
|
|
12
|
+
const { description, repositoryPath, attachments, approvalGates, push, openPr, parentId } = input;
|
|
13
|
+
if (!description?.trim()) {
|
|
14
|
+
return { error: 'description is required' };
|
|
18
15
|
}
|
|
19
16
|
if (!repositoryPath?.trim()) {
|
|
20
17
|
return { error: 'repositoryPath is required' };
|
|
21
18
|
}
|
|
22
|
-
const userInput = composeUserInput(
|
|
19
|
+
const userInput = composeUserInput(description, attachments);
|
|
23
20
|
const gates = {
|
|
24
21
|
allowPrd: approvalGates?.allowPrd ?? false,
|
|
25
22
|
allowPlan: approvalGates?.allowPlan ?? false,
|
|
@@ -35,7 +32,6 @@ export async function createFeature(input) {
|
|
|
35
32
|
push: push ?? false,
|
|
36
33
|
openPr: openPr ?? false,
|
|
37
34
|
...(parentId ? { parentId } : {}),
|
|
38
|
-
name,
|
|
39
35
|
description,
|
|
40
36
|
});
|
|
41
37
|
// Phase 2 (background): metadata generation, worktree, spec, agent spawn
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-create-drawer.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.tsx"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0DAA0D,CAAC;AAC/F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAC;AAG5E,YAAY,EAAE,cAAc,EAAE,MAAM,0DAA0D,CAAC;AAE/F,0DAA0D;AAC1D,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,oBAAoB;IACnC,
|
|
1
|
+
{"version":3,"file":"feature-create-drawer.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.tsx"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0DAA0D,CAAC;AAC/F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAC;AAG5E,YAAY,EAAE,cAAc,EAAE,MAAM,0DAA0D,CAAC;AAE/F,0DAA0D;AAC1D,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,cAAc,EAAE,CAAC;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE;QACb,QAAQ,EAAE,OAAO,CAAC;QAClB,SAAS,EAAE,OAAO,CAAC;QACnB,UAAU,EAAE,OAAO,CAAC;KACrB,CAAC;IACF,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAcD,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAC/C,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,qEAAqE;IACrE,QAAQ,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACjC,kGAAkG;IAClG,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,cAAc,EACd,YAAoB,EACpB,gBAAgB,EAChB,QAAQ,EACR,eAAe,GAChB,EAAE,wBAAwB,2CA4P1B"}
|
package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.js
CHANGED
|
@@ -31,7 +31,6 @@ export function FeatureCreateDrawer({ open, onClose, onSubmit, repositoryPath, i
|
|
|
31
31
|
const defaultGates = workflowDefaults?.approvalGates ?? EMPTY_GATES;
|
|
32
32
|
const defaultPush = workflowDefaults?.push ?? false;
|
|
33
33
|
const defaultOpenPr = workflowDefaults?.openPr ?? false;
|
|
34
|
-
const [name, setName] = useState('');
|
|
35
34
|
const [description, setDescription] = useState('');
|
|
36
35
|
const [attachments, setAttachments] = useState([]);
|
|
37
36
|
const [approvalGates, setApprovalGates] = useState({ ...defaultGates });
|
|
@@ -53,7 +52,6 @@ export function FeatureCreateDrawer({ open, onClose, onSubmit, repositoryPath, i
|
|
|
53
52
|
}
|
|
54
53
|
}, [open, initialParentId]);
|
|
55
54
|
const resetForm = useCallback(() => {
|
|
56
|
-
setName('');
|
|
57
55
|
setDescription('');
|
|
58
56
|
setAttachments([]);
|
|
59
57
|
setApprovalGates({ ...defaultGates });
|
|
@@ -62,18 +60,16 @@ export function FeatureCreateDrawer({ open, onClose, onSubmit, repositoryPath, i
|
|
|
62
60
|
setParentId(undefined);
|
|
63
61
|
}, [defaultGates, defaultPush, defaultOpenPr]);
|
|
64
62
|
// Track whether the form has unsaved data
|
|
65
|
-
const isDirty =
|
|
63
|
+
const isDirty = description.trim() !== '' || attachments.length > 0;
|
|
66
64
|
// Shared close guard — shows confirmation when dirty, prevents navigation
|
|
67
65
|
const { attemptClose } = useGuardedDrawerClose({ open, isDirty, onClose, onReset: resetForm });
|
|
68
66
|
const handleSubmit = useCallback((e) => {
|
|
69
67
|
e.preventDefault();
|
|
70
|
-
if (!
|
|
68
|
+
if (!description.trim())
|
|
71
69
|
return;
|
|
72
70
|
createSound.play();
|
|
73
|
-
const trimmedDescription = description.trim() || undefined;
|
|
74
71
|
onSubmit({
|
|
75
|
-
|
|
76
|
-
description: trimmedDescription,
|
|
72
|
+
description: description.trim(),
|
|
77
73
|
attachments,
|
|
78
74
|
repositoryPath,
|
|
79
75
|
approvalGates: {
|
|
@@ -87,7 +83,6 @@ export function FeatureCreateDrawer({ open, onClose, onSubmit, repositoryPath, i
|
|
|
87
83
|
});
|
|
88
84
|
resetForm();
|
|
89
85
|
}, [
|
|
90
|
-
name,
|
|
91
86
|
description,
|
|
92
87
|
attachments,
|
|
93
88
|
approvalGates,
|
|
@@ -114,7 +109,7 @@ export function FeatureCreateDrawer({ open, onClose, onSubmit, repositoryPath, i
|
|
|
114
109
|
setAttachments((prev) => prev.filter((f) => f.path !== path));
|
|
115
110
|
}, []);
|
|
116
111
|
const hasFeatures = features && features.length > 0;
|
|
117
|
-
return (_jsx(BaseDrawer, { open: open, onClose: attemptClose, size: "md", modal: false, dismissOnOutsideClick: true, "data-testid": "feature-create-drawer", header: _jsxs(_Fragment, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "h-2.5 w-2.5 shrink-0 rounded-full bg-blue-500" }), _jsx(DrawerTitle, { children: "NEW FEATURE" })] }), isSubmitting ? (_jsx(DrawerDescription, { asChild: true, children: _jsx("div", { children: _jsx(Badge, { variant: "secondary", children: "Creating..." }) }) })) : null] }), footer: _jsxs("div", { className: "flex flex-row justify-end gap-2", children: [_jsx(Button, { variant: "outline", onClick: attemptClose, disabled: isSubmitting, children: "Cancel" }), _jsx(Button, { type: "submit", form: "create-feature-form", disabled: !
|
|
112
|
+
return (_jsx(BaseDrawer, { open: open, onClose: attemptClose, size: "md", modal: false, dismissOnOutsideClick: true, "data-testid": "feature-create-drawer", header: _jsxs(_Fragment, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "h-2.5 w-2.5 shrink-0 rounded-full bg-blue-500" }), _jsx(DrawerTitle, { children: "NEW FEATURE" })] }), isSubmitting ? (_jsx(DrawerDescription, { asChild: true, children: _jsx("div", { children: _jsx(Badge, { variant: "secondary", children: "Creating..." }) }) })) : null] }), footer: _jsxs("div", { className: "flex flex-row justify-end gap-2", children: [_jsx(Button, { variant: "outline", onClick: attemptClose, disabled: isSubmitting, children: "Cancel" }), _jsx(Button, { type: "submit", form: "create-feature-form", disabled: !description.trim() || isSubmitting, children: isSubmitting ? 'Creating...' : '+ Create Feature' })] }), children: _jsx("div", { className: "overflow-y-auto p-4", children: _jsxs("form", { id: "create-feature-form", onSubmit: handleSubmit, className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Label, { htmlFor: "feature-description", className: "text-muted-foreground text-xs font-semibold tracking-wider", children: "DESCRIBE YOUR FEATURE" }), _jsx(Textarea, { id: "feature-description", placeholder: "e.g. Add GitHub OAuth login with callback handling and token refresh...", value: description, onChange: (e) => setDescription(e.target.value), rows: 4, required: true, disabled: isSubmitting })] }), hasFeatures ? (_jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Label, { htmlFor: "parent-feature", className: "text-muted-foreground text-xs font-semibold tracking-wider", children: "PARENT FEATURE" }), _jsx(ParentFeatureCombobox, { features: features, value: parentId, onChange: setParentId, disabled: isSubmitting })] })) : null, _jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Label, { className: "text-muted-foreground text-xs font-semibold tracking-wider", children: "APPROVE" }), _jsx(CheckboxGroup, { label: "Autonomous Mode", description: "YOLO!", parentAriaLabel: "Auto approve all", options: AUTO_APPROVE_OPTIONS, value: approvalGates, onValueChange: setApprovalGates, disabled: isSubmitting })] }), _jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Label, { className: "text-muted-foreground text-xs font-semibold tracking-wider", children: "GIT" }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(CheckboxGroupItem, { id: "push", label: "Push", description: "Push branch to remote after implementation.", checked: push || openPr, onCheckedChange: setPush, disabled: openPr || isSubmitting }), _jsx(CheckboxGroupItem, { id: "open-pr", label: "Create PR", description: "Open a pull request after pushing.", checked: openPr, onCheckedChange: setOpenPr, disabled: isSubmitting })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs(Label, { className: "text-muted-foreground text-xs font-semibold tracking-wider", children: ["ATTACHMENTS", attachments.length > 0 && (_jsxs("span", { className: "text-muted-foreground/60 ml-1.5", children: ["(", attachments.length, ")"] }))] }), _jsxs(Button, { type: "button", variant: "outline", size: "xs", onClick: handleAddFiles, disabled: isSubmitting, children: [_jsx(PlusIcon, { className: "size-3" }), "Add Files"] })] }), attachments.length > 0 && (_jsx("div", { className: "flex flex-col gap-2", children: attachments.map((file) => (_jsx(AttachmentCard, { file: file, onRemove: () => handleRemoveFile(file.path), disabled: isSubmitting }, file.path))) }))] })] }) }) }));
|
|
118
113
|
}
|
|
119
114
|
function ParentFeatureCombobox({ features, value, onChange, disabled, }) {
|
|
120
115
|
const [open, setOpen] = useState(false);
|
|
@@ -5,8 +5,7 @@ import { FeatureCreateDrawer } from './feature-create-drawer.js';
|
|
|
5
5
|
* create a new feature in the Control Center.
|
|
6
6
|
*
|
|
7
7
|
* ### Form sections
|
|
8
|
-
* - **
|
|
9
|
-
* - **Description** (optional) — multi-line textarea
|
|
8
|
+
* - **Description** (required) — multi-line textarea, submit is disabled when empty
|
|
10
9
|
* - **Auto Approve** — Tri-state parent checkbox with 3 child checkboxes (PRD, Plan, Merge).
|
|
11
10
|
* Parent shows indeterminate when some children are selected, checks/unchecks all on click.
|
|
12
11
|
* - **Attachments** (optional) — native OS file picker with extension-based icon system:
|
|
@@ -18,12 +17,12 @@ import { FeatureCreateDrawer } from './feature-create-drawer.js';
|
|
|
18
17
|
* |------|------|-------------|
|
|
19
18
|
* | `open` | `boolean` | Controls drawer visibility |
|
|
20
19
|
* | `onClose` | `() => void` | Called on dismiss (close button, cancel, or backdrop) |
|
|
21
|
-
* | `onSubmit` | `(data: FeatureCreatePayload) => void` | Called with `{
|
|
20
|
+
* | `onSubmit` | `(data: FeatureCreatePayload) => void` | Called with `{ description, attachments, repositoryPath, approvalGates }` |
|
|
22
21
|
* | `repositoryPath` | `string` | Repository path (mandatory) included in the submitted data |
|
|
23
22
|
*
|
|
24
23
|
* ### Behavior
|
|
25
|
-
* - Form resets (
|
|
26
|
-
* - Submit button is disabled when
|
|
24
|
+
* - Form resets (description, attachments, checkboxes) when the drawer closes
|
|
25
|
+
* - Submit button is disabled when description is empty
|
|
27
26
|
* - `approvalGates` always included: `{ allowPrd, allowPlan, allowMerge }` (all false by default)
|
|
28
27
|
* - Non-modal (`modal={false}`) — canvas stays interactive behind the drawer
|
|
29
28
|
* - Fixed width: 448px (`w-xl`) — matches review drawers (PRD, Plan, Merge)
|
|
@@ -36,13 +35,13 @@ type Story = StoryObj<typeof FeatureCreateDrawer>;
|
|
|
36
35
|
/** Default empty form — click the trigger button to open the create feature drawer. */
|
|
37
36
|
export declare const Default: Story;
|
|
38
37
|
/**
|
|
39
|
-
* Pre-filled form — click to open, then
|
|
38
|
+
* Pre-filled form — click to open, then description is typed in.
|
|
40
39
|
* Demonstrates the form in a "ready to submit" state.
|
|
41
40
|
*/
|
|
42
41
|
export declare const PreFilled: Story;
|
|
43
42
|
/**
|
|
44
|
-
* Validation state — open the drawer with an empty
|
|
45
|
-
* The "+ Create Feature" button is disabled because
|
|
43
|
+
* Validation state — open the drawer with an empty description field.
|
|
44
|
+
* The "+ Create Feature" button is disabled because description validation fails.
|
|
46
45
|
*/
|
|
47
46
|
export declare const ValidationDisabled: Story;
|
|
48
47
|
/** Click to open the drawer for quick visual inspection. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-create-drawer.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAU9D
|
|
1
|
+
{"version":3,"file":"feature-create-drawer.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAU9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,mBAAmB,CAgC1C,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAiDlD,uFAAuF;AACvF,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,SAAS,EAAE,KAiBvB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAMhC,CAAC;AAEF,4DAA4D;AAC5D,eAAO,MAAM,SAAS,EAAE,KAMvB,CAAC;AAMF;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,KAM1B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,UAAU,EAAE,KAUxB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,OAAO,EAAE,KAUrB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,SAAS,EAAE,KAUvB,CAAC;AAMF;;;GAGG;AACH,eAAO,MAAM,QAAQ,EAAE,KAUtB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,SAAS,EAAE,KAUvB,CAAC;AAYF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,EAAE,KAWlC,CAAC;AAgDF,+EAA+E;AAC/E,eAAO,MAAM,QAAQ,EAAE,KAEtB,CAAC;AAMF;;;GAGG;AACH,eAAO,MAAM,mBAAmB,EAAE,KAgBjC,CAAC;AAMF,eAAO,MAAM,WAAW,EAAE,KAsCzB,CAAC"}
|
|
@@ -12,8 +12,7 @@ import { DrawerCloseGuardProvider } from '../../../hooks/drawer-close-guard.js';
|
|
|
12
12
|
* create a new feature in the Control Center.
|
|
13
13
|
*
|
|
14
14
|
* ### Form sections
|
|
15
|
-
* - **
|
|
16
|
-
* - **Description** (optional) — multi-line textarea
|
|
15
|
+
* - **Description** (required) — multi-line textarea, submit is disabled when empty
|
|
17
16
|
* - **Auto Approve** — Tri-state parent checkbox with 3 child checkboxes (PRD, Plan, Merge).
|
|
18
17
|
* Parent shows indeterminate when some children are selected, checks/unchecks all on click.
|
|
19
18
|
* - **Attachments** (optional) — native OS file picker with extension-based icon system:
|
|
@@ -25,12 +24,12 @@ import { DrawerCloseGuardProvider } from '../../../hooks/drawer-close-guard.js';
|
|
|
25
24
|
* |------|------|-------------|
|
|
26
25
|
* | `open` | `boolean` | Controls drawer visibility |
|
|
27
26
|
* | `onClose` | `() => void` | Called on dismiss (close button, cancel, or backdrop) |
|
|
28
|
-
* | `onSubmit` | `(data: FeatureCreatePayload) => void` | Called with `{
|
|
27
|
+
* | `onSubmit` | `(data: FeatureCreatePayload) => void` | Called with `{ description, attachments, repositoryPath, approvalGates }` |
|
|
29
28
|
* | `repositoryPath` | `string` | Repository path (mandatory) included in the submitted data |
|
|
30
29
|
*
|
|
31
30
|
* ### Behavior
|
|
32
|
-
* - Form resets (
|
|
33
|
-
* - Submit button is disabled when
|
|
31
|
+
* - Form resets (description, attachments, checkboxes) when the drawer closes
|
|
32
|
+
* - Submit button is disabled when description is empty
|
|
34
33
|
* - `approvalGates` always included: `{ allowPrd, allowPlan, allowMerge }` (all false by default)
|
|
35
34
|
* - Non-modal (`modal={false}`) — canvas stays interactive behind the drawer
|
|
36
35
|
* - Fixed width: 448px (`w-xl`) — matches review drawers (PRD, Plan, Merge)
|
|
@@ -56,7 +55,7 @@ const meta = {
|
|
|
56
55
|
description: 'Callback fired when the drawer is dismissed (close button, cancel, or backdrop click).',
|
|
57
56
|
},
|
|
58
57
|
onSubmit: {
|
|
59
|
-
description: 'Callback fired with `FeatureCreatePayload` when the form is submitted. Receives `{
|
|
58
|
+
description: 'Callback fired with `FeatureCreatePayload` when the form is submitted. Receives `{ description, attachments, repositoryPath, approvalGates }`.',
|
|
60
59
|
},
|
|
61
60
|
repositoryPath: {
|
|
62
61
|
control: 'text',
|
|
@@ -92,7 +91,7 @@ export const Default = {
|
|
|
92
91
|
render: () => _jsx(CreateDrawerTrigger, {}),
|
|
93
92
|
};
|
|
94
93
|
/**
|
|
95
|
-
* Pre-filled form — click to open, then
|
|
94
|
+
* Pre-filled form — click to open, then description is typed in.
|
|
96
95
|
* Demonstrates the form in a "ready to submit" state.
|
|
97
96
|
*/
|
|
98
97
|
export const PreFilled = {
|
|
@@ -102,15 +101,13 @@ export const PreFilled = {
|
|
|
102
101
|
await userEvent.click(canvas.getByRole('button', { name: 'Open Pre-Filled' }));
|
|
103
102
|
// Drawer renders in a portal — query from document body
|
|
104
103
|
const body = within(canvasElement.ownerDocument.body);
|
|
105
|
-
const
|
|
106
|
-
const descInput = body.getByPlaceholderText('Describe what this feature does...');
|
|
107
|
-
await userEvent.type(nameInput, 'GitHub OAuth Login');
|
|
104
|
+
const descInput = await body.findByPlaceholderText('e.g. Add GitHub OAuth login with callback handling and token refresh...');
|
|
108
105
|
await userEvent.type(descInput, 'Implement OAuth2 authentication with GitHub as the identity provider. Includes login, callback handling, and token refresh.');
|
|
109
106
|
},
|
|
110
107
|
};
|
|
111
108
|
/**
|
|
112
|
-
* Validation state — open the drawer with an empty
|
|
113
|
-
* The "+ Create Feature" button is disabled because
|
|
109
|
+
* Validation state — open the drawer with an empty description field.
|
|
110
|
+
* The "+ Create Feature" button is disabled because description validation fails.
|
|
114
111
|
*/
|
|
115
112
|
export const ValidationDisabled = {
|
|
116
113
|
render: () => _jsx(CreateDrawerTrigger, { label: "Open Validation Demo" }),
|
|
@@ -243,7 +240,7 @@ export const WithWorkflowDefaults = {
|
|
|
243
240
|
* and click "Add Files" to attach files via the native OS file picker.
|
|
244
241
|
*
|
|
245
242
|
* **Submitted data** is displayed in a styled panel showing `FeatureCreatePayload`
|
|
246
|
-
* with `{
|
|
243
|
+
* with `{ description, attachments, repositoryPath, approvalGates }`.
|
|
247
244
|
*
|
|
248
245
|
* In Storybook, the native picker won't work (no backend), but submitted data
|
|
249
246
|
* would show the paths if files were attached programmatically.
|
|
@@ -278,8 +275,8 @@ export const DiscardConfirmation = {
|
|
|
278
275
|
const canvas = within(canvasElement);
|
|
279
276
|
await userEvent.click(canvas.getByRole('button', { name: 'Open (Discard Confirmation)' }));
|
|
280
277
|
const body = within(canvasElement.ownerDocument.body);
|
|
281
|
-
const
|
|
282
|
-
await userEvent.type(
|
|
278
|
+
const descInput = await body.findByPlaceholderText('e.g. Add GitHub OAuth login with callback handling and token refresh...');
|
|
279
|
+
await userEvent.type(descInput, 'My Feature');
|
|
283
280
|
// Click Cancel — should show the discard confirmation dialog
|
|
284
281
|
const cancelButton = body.getByRole('button', { name: 'Cancel' });
|
|
285
282
|
await userEvent.click(cancelButton);
|
package/dist/src/presentation/web/components/features/control-center/control-center-inner.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control-center-inner.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/control-center-inner.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,IAAI,
|
|
1
|
+
{"version":3,"file":"control-center-inner.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/control-center-inner.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,IAAI,EAAY,MAAM,eAAe,CAAC;AAEpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAc5E,UAAU,uBAAuB;IAC/B,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B,YAAY,EAAE,IAAI,EAAE,CAAC;CACtB;AAED,wBAAgB,kBAAkB,CAAC,EAAE,YAAY,EAAE,YAAY,EAAE,EAAE,uBAAuB,2CAoPzF"}
|
package/dist/src/presentation/web/components/features/control-center/control-center-inner.js
CHANGED
|
@@ -7,6 +7,7 @@ import { useSidebarFeaturesContext, mapNodeStateToSidebarStatus, } from '../../.
|
|
|
7
7
|
import { useSelectedFeatureId } from '../../../hooks/use-selected-feature-id.js';
|
|
8
8
|
import { useSoundAction } from '../../../hooks/use-sound-action.js';
|
|
9
9
|
import { useDrawerCloseGuard } from '../../../hooks/drawer-close-guard.js';
|
|
10
|
+
import { useViewportPersistence } from '../../../hooks/use-viewport-persistence.js';
|
|
10
11
|
import { ControlCenterEmptyState } from './control-center-empty-state.js';
|
|
11
12
|
import { useControlCenterState } from './use-control-center-state.js';
|
|
12
13
|
export function ControlCenterInner({ initialNodes, initialEdges }) {
|
|
@@ -15,6 +16,7 @@ export function ControlCenterInner({ initialNodes, initialEdges }) {
|
|
|
15
16
|
const selectedFeatureId = useSelectedFeatureId();
|
|
16
17
|
const clickSound = useSoundAction('click');
|
|
17
18
|
const { guardedNavigate } = useDrawerCloseGuard();
|
|
19
|
+
const { defaultViewport, onMoveEnd: handleViewportChange, resetViewport, } = useViewportPersistence();
|
|
18
20
|
const { nodes, edges, onNodesChange, handleConnect, handleAddRepository, handleDeleteFeature, handleDeleteRepository, createFeatureNode, setCallbacks, } = useControlCenterState(initialNodes, initialEdges);
|
|
19
21
|
// Publish sidebar features to context whenever feature node data changes
|
|
20
22
|
const { setFeatures: setSidebarFeatures } = useSidebarFeaturesContext();
|
|
@@ -158,9 +160,12 @@ export function ControlCenterInner({ initialNodes, initialEdges }) {
|
|
|
158
160
|
handleRepositoryClick,
|
|
159
161
|
handleDeleteRepository,
|
|
160
162
|
]);
|
|
163
|
+
const handleMoveEnd = useCallback((_event, viewport) => {
|
|
164
|
+
handleViewportChange(viewport);
|
|
165
|
+
}, [handleViewportChange]);
|
|
161
166
|
const hasRepositories = nodes.some((n) => n.type === 'repositoryNode');
|
|
162
167
|
if (!hasRepositories) {
|
|
163
168
|
return _jsx(ControlCenterEmptyState, { onRepositorySelect: handleAddRepository });
|
|
164
169
|
}
|
|
165
|
-
return (_jsx(FeaturesCanvas, { nodes: nodes, edges: edges, selectedFeatureId: selectedFeatureId, onNodesChange: onNodesChange, onConnect: handleConnect, onAddFeature: handleAddFeature, onNodeClick: handleNodeClick, onPaneClick: handleClearDrawers, emptyState: _jsx(ControlCenterEmptyState, { onRepositorySelect: handleAddRepository }) }));
|
|
170
|
+
return (_jsx(FeaturesCanvas, { nodes: nodes, edges: edges, selectedFeatureId: selectedFeatureId, defaultViewport: defaultViewport, onNodesChange: onNodesChange, onConnect: handleConnect, onAddFeature: handleAddFeature, onNodeClick: handleNodeClick, onPaneClick: handleClearDrawers, onMoveEnd: handleMoveEnd, onResetViewport: resetViewport, emptyState: _jsx(ControlCenterEmptyState, { onRepositorySelect: handleAddRepository }) }));
|
|
166
171
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dependency-edge.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/features-canvas/dependency-edge.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAMvD,OAAO,8BAA8B,CAAC;AAEtC,QAAA,MAAM,IAAI,EAAE,IAWX,CAAC;AAEF,eAAe,IAAI,CAAC;AA2BpB,eAAO,MAAM,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"dependency-edge.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/features-canvas/dependency-edge.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAMvD,OAAO,8BAA8B,CAAC;AAEtC,QAAA,MAAM,IAAI,EAAE,IAWX,CAAC;AAEF,eAAe,IAAI,CAAC;AA2BpB,eAAO,MAAM,OAAO,EAAE,QAiCrB,CAAC"}
|
package/dist/src/presentation/web/components/features/features-canvas/dependency-edge.stories.js
CHANGED
|
@@ -54,5 +54,5 @@ export const Default = {
|
|
|
54
54
|
target: 'child',
|
|
55
55
|
type: 'dependencyEdge',
|
|
56
56
|
},
|
|
57
|
-
], nodeTypes: { featureNode: FeatureNode }, edgeTypes: { dependencyEdge: DependencyEdge }, fitView: true, nodesDraggable: false, nodesConnectable: false })),
|
|
57
|
+
], nodeTypes: { featureNode: FeatureNode }, edgeTypes: { dependencyEdge: DependencyEdge }, fitView: true, nodesDraggable: false, nodesConnectable: false, proOptions: { hideAttribution: true } })),
|
|
58
58
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Connection, Edge, NodeChange } from '@xyflow/react';
|
|
1
|
+
import type { Connection, Edge, NodeChange, OnMoveEnd, Viewport } 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
4
|
export type CanvasNodeType = FeatureNodeType | RepositoryNodeType;
|
|
@@ -6,14 +6,17 @@ export interface FeaturesCanvasProps {
|
|
|
6
6
|
nodes: CanvasNodeType[];
|
|
7
7
|
edges: Edge[];
|
|
8
8
|
selectedFeatureId?: string | null;
|
|
9
|
+
defaultViewport?: Viewport;
|
|
9
10
|
onNodesChange?: (changes: NodeChange<CanvasNodeType>[]) => void;
|
|
10
11
|
onAddFeature?: () => void;
|
|
11
12
|
onNodeClick?: (event: React.MouseEvent, node: CanvasNodeType) => void;
|
|
12
13
|
onPaneClick?: (event: React.MouseEvent) => void;
|
|
13
14
|
onConnect?: (connection: Connection) => void;
|
|
14
15
|
onCanvasDrag?: () => void;
|
|
16
|
+
onMoveEnd?: OnMoveEnd;
|
|
17
|
+
onResetViewport?: () => Viewport;
|
|
15
18
|
toolbar?: React.ReactNode;
|
|
16
19
|
emptyState?: React.ReactNode;
|
|
17
20
|
}
|
|
18
|
-
export declare function FeaturesCanvas({ nodes, edges, selectedFeatureId, onNodesChange, onAddFeature, onConnect, onNodeClick, onPaneClick, onCanvasDrag, toolbar, emptyState, }: FeaturesCanvasProps): import("react/jsx-runtime").JSX.Element;
|
|
21
|
+
export declare function FeaturesCanvas({ nodes, edges, selectedFeatureId, defaultViewport, onNodesChange, onAddFeature, onConnect, onNodeClick, onPaneClick, onCanvasDrag, onMoveEnd, onResetViewport, toolbar, emptyState, }: FeaturesCanvasProps): import("react/jsx-runtime").JSX.Element;
|
|
19
22
|
//# sourceMappingURL=features-canvas.d.ts.map
|
package/dist/src/presentation/web/components/features/features-canvas/features-canvas.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features-canvas.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/features-canvas/features-canvas.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"features-canvas.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/features-canvas/features-canvas.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAKvF,OAAO,KAAK,EAAE,eAAe,EAAmB,MAAM,kCAAkC,CAAC;AAEzF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAG9E,MAAM,MAAM,cAAc,GAAG,eAAe,GAAG,kBAAkB,CAAC;AAElE,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,eAAe,CAAC,EAAE,QAAQ,CAAC;IAC3B,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,cAAc,CAAC,EAAE,KAAK,IAAI,CAAC;IAChE,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,UAAU,EAAE,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IACtE,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IAChD,SAAS,CAAC,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;IAC7C,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,QAAQ,CAAC;IACjC,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC9B;AAmBD,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,KAAK,EACL,iBAAiB,EACjB,eAAe,EACf,aAAa,EACb,YAAY,EACZ,SAAS,EACT,WAAW,EACX,WAAW,EACX,YAAY,EACZ,SAAS,EACT,eAAe,EACf,OAAO,EACP,UAAU,GACX,EAAE,mBAAmB,2CAsFrB"}
|
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useMemo } from 'react';
|
|
4
|
-
import { ReactFlow, Background, Controls } from '@xyflow/react';
|
|
5
|
-
import { Plus } from 'lucide-react';
|
|
3
|
+
import { useCallback, useMemo } from 'react';
|
|
4
|
+
import { ReactFlow, Background, Controls, ControlButton, useReactFlow } from '@xyflow/react';
|
|
5
|
+
import { Plus, RotateCcw } from 'lucide-react';
|
|
6
6
|
import { Button } from '../../ui/button.js';
|
|
7
7
|
import { EmptyState } from '../../common/empty-state/index.js';
|
|
8
8
|
import { FeatureNode } from '../../common/feature-node/index.js';
|
|
9
9
|
import { RepositoryNode } from '../../common/repository-node/index.js';
|
|
10
10
|
import { DependencyEdge } from './dependency-edge.js';
|
|
11
|
-
|
|
11
|
+
const FALLBACK_VIEWPORT = { x: 30, y: 30, zoom: 0.85 };
|
|
12
|
+
function ResetButton({ onResetViewport }) {
|
|
13
|
+
const { setViewport } = useReactFlow();
|
|
14
|
+
const handleReset = useCallback(() => {
|
|
15
|
+
const viewport = onResetViewport();
|
|
16
|
+
setViewport(viewport, { duration: 400 });
|
|
17
|
+
}, [onResetViewport, setViewport]);
|
|
18
|
+
return (_jsx(ControlButton, { onClick: handleReset, title: "Reset view", "aria-label": "Reset view", children: _jsx(RotateCcw, {}) }));
|
|
19
|
+
}
|
|
20
|
+
export function FeaturesCanvas({ nodes, edges, selectedFeatureId, defaultViewport, onNodesChange, onAddFeature, onConnect, onNodeClick, onPaneClick, onCanvasDrag, onMoveEnd, onResetViewport, toolbar, emptyState, }) {
|
|
12
21
|
const nodeTypes = useMemo(() => ({
|
|
13
22
|
featureNode: FeatureNode,
|
|
14
23
|
repositoryNode: RepositoryNode,
|
|
@@ -30,5 +39,5 @@ export function FeaturesCanvas({ nodes, edges, selectedFeatureId, onNodesChange,
|
|
|
30
39
|
}
|
|
31
40
|
return (_jsx("div", { "data-testid": "features-canvas-empty", children: _jsx(EmptyState, { title: "No features yet", description: "Get started by creating your first feature.", action: _jsxs(Button, { onClick: onAddFeature, children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), "New Feature"] }) }) }));
|
|
32
41
|
}
|
|
33
|
-
return (_jsx("div", { "data-testid": "features-canvas", "data-no-drawer-close": true, className: "pointer-events-auto h-full w-full", children: _jsxs(ReactFlow, { nodes: enrichedNodes, edges: edges, nodeTypes: nodeTypes, edgeTypes: edgeTypes, onConnect: onConnect, onNodesChange: onNodesChange, onNodeClick: onNodeClick, onPaneClick: onPaneClick, onMoveStart: onCanvasDrag,
|
|
42
|
+
return (_jsx("div", { "data-testid": "features-canvas", "data-no-drawer-close": true, className: "pointer-events-auto h-full w-full", children: _jsxs(ReactFlow, { nodes: enrichedNodes, edges: edges, nodeTypes: nodeTypes, edgeTypes: edgeTypes, onConnect: onConnect, onNodesChange: onNodesChange, onNodeClick: onNodeClick, onPaneClick: onPaneClick, onMoveStart: onCanvasDrag, onMoveEnd: onMoveEnd, defaultViewport: defaultViewport ?? FALLBACK_VIEWPORT, nodesDraggable: false, nodesConnectable: false, elementsSelectable: false, proOptions: { hideAttribution: true }, children: [_jsx(Background, {}), _jsx(Controls, { showInteractive: false, children: onResetViewport ? _jsx(ResetButton, { onResetViewport: onResetViewport }) : null }), toolbar] }) }));
|
|
34
43
|
}
|
package/dist/src/presentation/web/components/features/features-canvas/features-canvas.stories.d.ts
CHANGED
|
@@ -14,4 +14,6 @@ export declare const SingleRepositoryMultipleFeatures: Story;
|
|
|
14
14
|
export declare const MultipleRepositories: Story;
|
|
15
15
|
export declare const MultipleRepositoriesMixedConnections: Story;
|
|
16
16
|
export declare const InteractiveWithRepository: Story;
|
|
17
|
+
export declare const WithSavedViewport: Story;
|
|
18
|
+
export declare const ZoomedOutViewport: Story;
|
|
17
19
|
//# sourceMappingURL=features-canvas.stories.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features-canvas.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/features-canvas/features-canvas.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAMnD,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,cAAc,CAcrC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAwKnC,eAAO,MAAM,KAAK,EAAE,KAMnB,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,KAKxB,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,KAK3B,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,KAK5B,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,KASzB,CAAC;AAoDF,eAAO,MAAM,mCAAmC,EAAE,KAKjD,CAAC;AA4BF,eAAO,MAAM,gCAAgC,EAAE,KAO9C,CAAC;AA0DF,eAAO,MAAM,gCAAgC,EAAE,KAS9C,CAAC;AAiDF,eAAO,MAAM,oBAAoB,EAAE,KAQlC,CAAC;AAqGF,eAAO,MAAM,oCAAoC,EAAE,KAKlD,CAAC;AAEF,eAAO,MAAM,yBAAyB,EAAE,KAMvC,CAAC"}
|
|
1
|
+
{"version":3,"file":"features-canvas.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/features-canvas/features-canvas.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAMnD,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,cAAc,CAcrC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAwKnC,eAAO,MAAM,KAAK,EAAE,KAMnB,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,KAKxB,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,KAK3B,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,KAK5B,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,KASzB,CAAC;AAoDF,eAAO,MAAM,mCAAmC,EAAE,KAKjD,CAAC;AA4BF,eAAO,MAAM,gCAAgC,EAAE,KAO9C,CAAC;AA0DF,eAAO,MAAM,gCAAgC,EAAE,KAS9C,CAAC;AAiDF,eAAO,MAAM,oBAAoB,EAAE,KAQlC,CAAC;AAqGF,eAAO,MAAM,oCAAoC,EAAE,KAKlD,CAAC;AAEF,eAAO,MAAM,yBAAyB,EAAE,KAMvC,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,KAO/B,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,KAO/B,CAAC"}
|
package/dist/src/presentation/web/components/features/features-canvas/features-canvas.stories.js
CHANGED
|
@@ -516,3 +516,19 @@ export const InteractiveWithRepository = {
|
|
|
516
516
|
onAddFeature: () => undefined,
|
|
517
517
|
},
|
|
518
518
|
};
|
|
519
|
+
export const WithSavedViewport = {
|
|
520
|
+
args: {
|
|
521
|
+
nodes: repoFeatureNodes,
|
|
522
|
+
edges: repoFeatureEdges,
|
|
523
|
+
defaultViewport: { x: 200, y: 150, zoom: 1.0 },
|
|
524
|
+
onResetViewport: () => ({ x: 30, y: 30, zoom: 0.85 }),
|
|
525
|
+
},
|
|
526
|
+
};
|
|
527
|
+
export const ZoomedOutViewport = {
|
|
528
|
+
args: {
|
|
529
|
+
nodes: mixedLayoutedNodes,
|
|
530
|
+
edges: mixedLayoutedEdges,
|
|
531
|
+
defaultViewport: { x: 0, y: 0, zoom: 0.5 },
|
|
532
|
+
onResetViewport: () => ({ x: 30, y: 30, zoom: 0.85 }),
|
|
533
|
+
},
|
|
534
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Viewport } from '@xyflow/react';
|
|
2
|
+
export declare const STORAGE_KEY = "shep-canvas-viewport";
|
|
3
|
+
export declare const DEBOUNCE_MS = 500;
|
|
4
|
+
export declare const DEFAULT_VIEWPORT: Viewport;
|
|
5
|
+
export interface UseViewportPersistenceResult {
|
|
6
|
+
defaultViewport: Viewport;
|
|
7
|
+
onMoveEnd: (viewport: Viewport) => void;
|
|
8
|
+
resetViewport: () => Viewport;
|
|
9
|
+
}
|
|
10
|
+
export declare function useViewportPersistence(): UseViewportPersistenceResult;
|
|
11
|
+
//# sourceMappingURL=use-viewport-persistence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-viewport-persistence.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/hooks/use-viewport-persistence.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C,eAAO,MAAM,WAAW,yBAAyB,CAAC;AAClD,eAAO,MAAM,WAAW,MAAM,CAAC;AAE/B,eAAO,MAAM,gBAAgB,EAAE,QAAuC,CAAC;AAEvE,MAAM,WAAW,4BAA4B;IAC3C,eAAe,EAAE,QAAQ,CAAC;IAC1B,SAAS,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC;IACxC,aAAa,EAAE,MAAM,QAAQ,CAAC;CAC/B;AA6BD,wBAAgB,sBAAsB,IAAI,4BAA4B,CAmCrE"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
3
|
+
export const STORAGE_KEY = 'shep-canvas-viewport';
|
|
4
|
+
export const DEBOUNCE_MS = 500;
|
|
5
|
+
export const DEFAULT_VIEWPORT = { x: 30, y: 30, zoom: 0.85 };
|
|
6
|
+
function isValidViewport(value) {
|
|
7
|
+
if (value == null || typeof value !== 'object' || Array.isArray(value)) {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
const v = value;
|
|
11
|
+
return (typeof v.x === 'number' &&
|
|
12
|
+
typeof v.y === 'number' &&
|
|
13
|
+
typeof v.zoom === 'number' &&
|
|
14
|
+
Number.isFinite(v.x) &&
|
|
15
|
+
Number.isFinite(v.y) &&
|
|
16
|
+
Number.isFinite(v.zoom) &&
|
|
17
|
+
v.zoom > 0);
|
|
18
|
+
}
|
|
19
|
+
function readViewport() {
|
|
20
|
+
try {
|
|
21
|
+
const raw = localStorage.getItem(STORAGE_KEY);
|
|
22
|
+
if (raw == null)
|
|
23
|
+
return DEFAULT_VIEWPORT;
|
|
24
|
+
const parsed = JSON.parse(raw);
|
|
25
|
+
return isValidViewport(parsed) ? parsed : DEFAULT_VIEWPORT;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return DEFAULT_VIEWPORT;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export function useViewportPersistence() {
|
|
32
|
+
const defaultViewport = useRef(readViewport()).current;
|
|
33
|
+
const timerRef = useRef(null);
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
return () => {
|
|
36
|
+
if (timerRef.current != null) {
|
|
37
|
+
clearTimeout(timerRef.current);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}, []);
|
|
41
|
+
const onMoveEnd = useCallback((viewport) => {
|
|
42
|
+
if (timerRef.current != null) {
|
|
43
|
+
clearTimeout(timerRef.current);
|
|
44
|
+
}
|
|
45
|
+
timerRef.current = setTimeout(() => {
|
|
46
|
+
try {
|
|
47
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(viewport));
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// Silently ignore localStorage errors (quota exceeded, private browsing, etc.)
|
|
51
|
+
}
|
|
52
|
+
}, DEBOUNCE_MS);
|
|
53
|
+
}, []);
|
|
54
|
+
const resetViewport = useCallback(() => {
|
|
55
|
+
try {
|
|
56
|
+
localStorage.removeItem(STORAGE_KEY);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// Silently ignore localStorage errors
|
|
60
|
+
}
|
|
61
|
+
return DEFAULT_VIEWPORT;
|
|
62
|
+
}, []);
|
|
63
|
+
return { defaultViewport, onMoveEnd, resetViewport };
|
|
64
|
+
}
|