@shepai/cli 1.62.0 → 1.63.0

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.
Files changed (201) hide show
  1. package/dist/packages/core/src/infrastructure/services/pr-sync/pr-sync-watcher.service.js +1 -1
  2. package/dist/src/presentation/web/components/common/base-drawer/base-drawer.d.ts +17 -0
  3. package/dist/src/presentation/web/components/common/base-drawer/base-drawer.d.ts.map +1 -0
  4. package/dist/src/presentation/web/components/common/base-drawer/base-drawer.js +23 -0
  5. package/dist/src/presentation/web/components/common/base-drawer/base-drawer.stories.d.ts +22 -0
  6. package/dist/src/presentation/web/components/common/base-drawer/base-drawer.stories.d.ts.map +1 -0
  7. package/dist/src/presentation/web/components/common/base-drawer/base-drawer.stories.js +56 -0
  8. package/dist/src/presentation/web/components/common/base-drawer/index.d.ts +2 -0
  9. package/dist/src/presentation/web/components/common/base-drawer/index.d.ts.map +1 -0
  10. package/dist/src/presentation/web/components/common/base-drawer/index.js +1 -0
  11. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.d.ts.map +1 -1
  12. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.js +9 -16
  13. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.d.ts +1 -1
  14. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.js +1 -1
  15. package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.d.ts.map +1 -1
  16. package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.js +14 -14
  17. package/dist/src/presentation/web/components/common/repository-node/index.d.ts +1 -0
  18. package/dist/src/presentation/web/components/common/repository-node/index.d.ts.map +1 -1
  19. package/dist/src/presentation/web/components/common/repository-node/index.js +1 -0
  20. package/dist/src/presentation/web/components/common/repository-node/repository-drawer.d.ts +7 -0
  21. package/dist/src/presentation/web/components/common/repository-node/repository-drawer.d.ts.map +1 -0
  22. package/dist/src/presentation/web/components/common/repository-node/repository-drawer.js +12 -0
  23. package/dist/src/presentation/web/components/common/repository-node/repository-drawer.stories.d.ts +9 -0
  24. package/dist/src/presentation/web/components/common/repository-node/repository-drawer.stories.d.ts.map +1 -0
  25. package/dist/src/presentation/web/components/common/repository-node/repository-drawer.stories.js +34 -0
  26. package/dist/src/presentation/web/components/common/review-drawer-shell/review-drawer-shell.d.ts.map +1 -1
  27. package/dist/src/presentation/web/components/common/review-drawer-shell/review-drawer-shell.js +4 -6
  28. package/dist/src/presentation/web/components/features/control-center/control-center-inner.d.ts.map +1 -1
  29. package/dist/src/presentation/web/components/features/control-center/control-center-inner.js +20 -2
  30. package/dist/src/presentation/web/components/features/features-canvas/features-canvas.d.ts +3 -1
  31. package/dist/src/presentation/web/components/features/features-canvas/features-canvas.d.ts.map +1 -1
  32. package/dist/src/presentation/web/components/features/features-canvas/features-canvas.js +4 -2
  33. package/dist/src/presentation/web/components/features/skills/skill-detail-drawer.d.ts.map +1 -1
  34. package/dist/src/presentation/web/components/features/skills/skill-detail-drawer.js +3 -6
  35. package/dist/tsconfig.build.tsbuildinfo +1 -1
  36. package/package.json +1 -1
  37. package/web/.next/BUILD_ID +1 -1
  38. package/web/.next/build-manifest.json +2 -2
  39. package/web/.next/cache/.previewinfo +1 -1
  40. package/web/.next/cache/.rscinfo +1 -1
  41. package/web/.next/cache/.tsbuildinfo +1 -1
  42. package/web/.next/cache/config.json +3 -3
  43. package/web/.next/fallback-build-manifest.json +2 -2
  44. package/web/.next/prerender-manifest.json +3 -3
  45. package/web/.next/required-server-files.js +1 -1
  46. package/web/.next/required-server-files.json +1 -1
  47. package/web/.next/server/app/_global-error.html +2 -2
  48. package/web/.next/server/app/_global-error.rsc +1 -1
  49. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  50. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  51. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  52. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  53. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  54. package/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  55. package/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  56. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  57. package/web/.next/server/app/page/server-reference-manifest.json +14 -14
  58. package/web/.next/server/app/page.js.nft.json +1 -1
  59. package/web/.next/server/app/page_client-reference-manifest.js +1 -1
  60. package/web/.next/server/app/skills/page/server-reference-manifest.json +1 -1
  61. package/web/.next/server/app/skills/page.js.nft.json +1 -1
  62. package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  63. package/web/.next/server/app/tools/page/server-reference-manifest.json +1 -1
  64. package/web/.next/server/app/tools/page.js.nft.json +1 -1
  65. package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  66. package/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
  67. package/web/.next/server/app/version/page.js.nft.json +1 -1
  68. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  69. package/web/.next/server/chunks/ssr/[root-of-the-server]__08ba9bd3._.js +1 -1
  70. package/web/.next/server/chunks/ssr/[root-of-the-server]__08ba9bd3._.js.map +1 -1
  71. package/web/.next/server/chunks/ssr/[root-of-the-server]__5e0f14e9._.js +3 -0
  72. package/web/.next/server/chunks/ssr/[root-of-the-server]__5e0f14e9._.js.map +1 -0
  73. package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +1 -1
  74. package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js.map +1 -1
  75. package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js +1 -1
  76. package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js.map +1 -1
  77. package/web/.next/server/chunks/ssr/[root-of-the-server]__9add7c3a._.js +4 -4
  78. package/web/.next/server/chunks/ssr/[root-of-the-server]__9add7c3a._.js.map +1 -1
  79. package/web/.next/server/chunks/ssr/[root-of-the-server]__ae251147._.js +3 -0
  80. package/web/.next/server/chunks/ssr/[root-of-the-server]__ae251147._.js.map +1 -0
  81. package/web/.next/server/chunks/ssr/[root-of-the-server]__b6839c3f._.js +2 -2
  82. package/web/.next/server/chunks/ssr/[root-of-the-server]__b6839c3f._.js.map +1 -1
  83. package/web/.next/server/chunks/ssr/[root-of-the-server]__da0ade1f._.js +1 -1
  84. package/web/.next/server/chunks/ssr/[root-of-the-server]__da0ade1f._.js.map +1 -1
  85. package/web/.next/server/chunks/ssr/[root-of-the-server]__dd5b62cb._.js +3 -0
  86. package/web/.next/server/chunks/ssr/[root-of-the-server]__dd5b62cb._.js.map +1 -0
  87. package/web/.next/server/chunks/ssr/[root-of-the-server]__fbc89707._.js +1 -1
  88. package/web/.next/server/chunks/ssr/_73d14b70._.js +3 -0
  89. package/web/.next/server/chunks/ssr/_73d14b70._.js.map +1 -0
  90. package/web/.next/server/chunks/ssr/_7f386377._.js +3 -0
  91. package/web/.next/server/chunks/ssr/_7f386377._.js.map +1 -0
  92. package/web/.next/server/chunks/ssr/_d3711354._.js +2 -2
  93. package/web/.next/server/chunks/ssr/_d3711354._.js.map +1 -1
  94. package/web/.next/server/chunks/ssr/{_a64b7b24._.js → _d81184e2._.js} +2 -2
  95. package/web/.next/server/chunks/ssr/_d81184e2._.js.map +1 -0
  96. package/web/.next/{standalone/src/presentation/web/.next/server/chunks/ssr/node_modules__pnpm_fe355030._.js → server/chunks/ssr/node_modules__pnpm_87f920e7._.js} +2 -2
  97. package/web/.next/server/chunks/ssr/{node_modules__pnpm_fe355030._.js.map → node_modules__pnpm_87f920e7._.js.map} +1 -1
  98. package/web/.next/server/chunks/ssr/src_presentation_web_components_7a0b09da._.js +3 -0
  99. package/web/.next/server/chunks/ssr/src_presentation_web_components_7a0b09da._.js.map +1 -0
  100. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +3 -0
  101. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js.map +1 -0
  102. package/web/.next/server/pages/500.html +2 -2
  103. package/web/.next/server/server-reference-manifest.js +1 -1
  104. package/web/.next/server/server-reference-manifest.json +15 -15
  105. package/web/.next/standalone/src/presentation/web/.next/BUILD_ID +1 -1
  106. package/web/.next/standalone/src/presentation/web/.next/build-manifest.json +2 -2
  107. package/web/.next/standalone/src/presentation/web/.next/prerender-manifest.json +3 -3
  108. package/web/.next/standalone/src/presentation/web/.next/required-server-files.json +1 -1
  109. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.html +2 -2
  110. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.rsc +1 -1
  111. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  112. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  113. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  114. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  115. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  116. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  117. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  118. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  119. package/web/.next/standalone/src/presentation/web/.next/server/app/page/server-reference-manifest.json +14 -14
  120. package/web/.next/standalone/src/presentation/web/.next/server/app/page.js.nft.json +1 -1
  121. package/web/.next/standalone/src/presentation/web/.next/server/app/page_client-reference-manifest.js +1 -1
  122. package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page/server-reference-manifest.json +1 -1
  123. package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page.js.nft.json +1 -1
  124. package/web/.next/standalone/src/presentation/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  125. package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page/server-reference-manifest.json +1 -1
  126. package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page.js.nft.json +1 -1
  127. package/web/.next/standalone/src/presentation/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  128. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
  129. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page.js.nft.json +1 -1
  130. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  131. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__08ba9bd3._.js +1 -1
  132. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__5e0f14e9._.js +3 -0
  133. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +1 -1
  134. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js +1 -1
  135. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__9add7c3a._.js +4 -4
  136. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__ae251147._.js +3 -0
  137. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__b6839c3f._.js +2 -2
  138. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__da0ade1f._.js +1 -1
  139. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__dd5b62cb._.js +3 -0
  140. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__fbc89707._.js +1 -1
  141. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_73d14b70._.js +3 -0
  142. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_7f386377._.js +3 -0
  143. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_d3711354._.js +2 -2
  144. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/{_a64b7b24._.js → _d81184e2._.js} +2 -2
  145. package/web/.next/{server/chunks/ssr/node_modules__pnpm_fe355030._.js → standalone/src/presentation/web/.next/server/chunks/ssr/node_modules__pnpm_87f920e7._.js} +2 -2
  146. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_components_7a0b09da._.js +3 -0
  147. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +3 -0
  148. package/web/.next/standalone/src/presentation/web/.next/server/pages/500.html +2 -2
  149. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.js +1 -1
  150. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.json +15 -15
  151. package/web/.next/standalone/src/presentation/web/components/common/base-drawer/base-drawer.stories.tsx +207 -0
  152. package/web/.next/standalone/src/presentation/web/components/common/base-drawer/base-drawer.tsx +90 -0
  153. package/web/.next/standalone/src/presentation/web/components/common/base-drawer/index.ts +1 -0
  154. package/web/.next/standalone/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.tsx +1 -1
  155. package/web/.next/standalone/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.tsx +143 -172
  156. package/web/.next/standalone/src/presentation/web/components/common/feature-drawer/feature-drawer.tsx +119 -133
  157. package/web/.next/standalone/src/presentation/web/components/common/repository-node/index.ts +1 -0
  158. package/web/.next/standalone/src/presentation/web/components/common/repository-node/repository-drawer.stories.tsx +58 -0
  159. package/web/.next/standalone/src/presentation/web/components/common/repository-node/repository-drawer.tsx +82 -0
  160. package/web/.next/standalone/src/presentation/web/components/common/review-drawer-shell/review-drawer-shell.tsx +79 -96
  161. package/web/.next/standalone/src/presentation/web/components/features/control-center/control-center-inner.tsx +30 -2
  162. package/web/.next/standalone/src/presentation/web/components/features/features-canvas/features-canvas.tsx +7 -0
  163. package/web/.next/standalone/src/presentation/web/components/features/skills/skill-detail-drawer.tsx +71 -77
  164. package/web/.next/standalone/src/presentation/web/server.js +1 -1
  165. package/web/.next/static/chunks/0c6654ec27f11c7e.js +1 -0
  166. package/web/.next/static/chunks/12c70bfd5951cf9b.js +1 -0
  167. package/web/.next/static/chunks/21541b346dd4dd28.js +1 -0
  168. package/web/.next/static/chunks/{cb1b27e4a21415d3.js → 3b941e59ac013e12.js} +2 -2
  169. package/web/.next/static/chunks/426292e5fb0b2a61.js +1 -0
  170. package/web/.next/static/chunks/{09d898be63c54f20.js → 78919481e7c5ad4f.js} +1 -1
  171. package/web/.next/static/chunks/a5b6a22de303e877.css +2 -0
  172. package/web/.next/static/chunks/af7a5bcb7c49e46e.js +1 -0
  173. package/web/.next/static/chunks/be784143669bb992.js +1 -0
  174. package/web/.next/static/chunks/ea57f9afe9055c7a.js +10 -0
  175. package/web/.next/trace +1 -1
  176. package/web/.next/trace-build +1 -1
  177. package/web/.next/server/chunks/ssr/[root-of-the-server]__2395adc6._.js +0 -3
  178. package/web/.next/server/chunks/ssr/[root-of-the-server]__2395adc6._.js.map +0 -1
  179. package/web/.next/server/chunks/ssr/[root-of-the-server]__87fda958._.js +0 -3
  180. package/web/.next/server/chunks/ssr/[root-of-the-server]__87fda958._.js.map +0 -1
  181. package/web/.next/server/chunks/ssr/[root-of-the-server]__ee7cffe1._.js +0 -3
  182. package/web/.next/server/chunks/ssr/[root-of-the-server]__ee7cffe1._.js.map +0 -1
  183. package/web/.next/server/chunks/ssr/_9915d2a7._.js +0 -3
  184. package/web/.next/server/chunks/ssr/_9915d2a7._.js.map +0 -1
  185. package/web/.next/server/chunks/ssr/_a64b7b24._.js.map +0 -1
  186. package/web/.next/server/chunks/ssr/_e7a4b0e4._.js +0 -3
  187. package/web/.next/server/chunks/ssr/_e7a4b0e4._.js.map +0 -1
  188. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__2395adc6._.js +0 -3
  189. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__87fda958._.js +0 -3
  190. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__ee7cffe1._.js +0 -3
  191. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_9915d2a7._.js +0 -3
  192. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_e7a4b0e4._.js +0 -3
  193. package/web/.next/static/chunks/13664c029245afc8.js +0 -1
  194. package/web/.next/static/chunks/25f42652257ff43d.js +0 -1
  195. package/web/.next/static/chunks/2934854f0378f815.js +0 -1
  196. package/web/.next/static/chunks/45f510f84c7c417d.css +0 -2
  197. package/web/.next/static/chunks/8e4c719503d9387e.js +0 -1
  198. package/web/.next/static/chunks/cd44f06f18e3425a.js +0 -10
  199. /package/web/.next/static/{2z-UoCok36_D1hh1-N88G → 23Y-ea-5g4_fUHZyGpCb0}/_buildManifest.js +0 -0
  200. /package/web/.next/static/{2z-UoCok36_D1hh1-N88G → 23Y-ea-5g4_fUHZyGpCb0}/_clientMiddlewareManifest.json +0 -0
  201. /package/web/.next/static/{2z-UoCok36_D1hh1-N88G → 23Y-ea-5g4_fUHZyGpCb0}/_ssgManifest.js +0 -0
@@ -1,17 +1,13 @@
1
1
  'use client';
2
2
 
3
- import { XIcon, Loader2, Trash2, ExternalLink, GitCommitHorizontal } from 'lucide-react';
3
+ import { useCallback, useEffect } from 'react';
4
+ import { Loader2, Trash2, ExternalLink, GitCommitHorizontal } from 'lucide-react';
4
5
  import { PrStatus } from '@shepai/core/domain/generated/output';
5
6
  import { cn } from '@/lib/utils';
6
7
  import { useSoundAction } from '@/hooks/use-sound-action';
7
8
  import { OpenActionMenu } from '@/components/common/open-action-menu';
8
- import {
9
- Drawer,
10
- DrawerContent,
11
- DrawerHeader,
12
- DrawerTitle,
13
- DrawerDescription,
14
- } from '@/components/ui/drawer';
9
+ import { BaseDrawer } from '@/components/common/base-drawer';
10
+ import { DrawerTitle, DrawerDescription } from '@/components/ui/drawer';
15
11
  import { Button } from '@/components/ui/button';
16
12
  import { Badge } from '@/components/ui/badge';
17
13
  import { Separator } from '@/components/ui/separator';
@@ -48,143 +44,133 @@ export function FeatureDrawer({
48
44
  const drawerOpenSound = useSoundAction('drawer-open');
49
45
  const drawerCloseSound = useSoundAction('drawer-close');
50
46
 
47
+ const isOpen = selectedNode !== null;
48
+ useEffect(() => {
49
+ if (isOpen) drawerOpenSound.play();
50
+ }, [isOpen, drawerOpenSound]);
51
+
52
+ const handleClose = useCallback(() => {
53
+ drawerCloseSound.play();
54
+ onClose();
55
+ }, [onClose, drawerCloseSound]);
56
+
51
57
  return (
52
- <Drawer
53
- direction="right"
54
- modal={false}
55
- handleOnly
58
+ <BaseDrawer
56
59
  open={selectedNode !== null}
57
- onOpenChange={(open) => {
58
- if (open) {
59
- drawerOpenSound.play();
60
- } else {
61
- drawerCloseSound.play();
62
- onClose();
63
- }
64
- }}
60
+ onClose={handleClose}
61
+ size="sm"
62
+ modal={false}
63
+ data-testid="feature-drawer"
64
+ header={
65
+ selectedNode ? (
66
+ <div data-testid="feature-drawer-header">
67
+ <DrawerTitle>{selectedNode.name}</DrawerTitle>
68
+ <DrawerDescription>{selectedNode.featureId}</DrawerDescription>
69
+ </div>
70
+ ) : undefined
71
+ }
65
72
  >
66
- <DrawerContent direction="right" className="w-96" showCloseButton={false}>
67
- {selectedNode ? (
68
- <>
69
- {/* Close button */}
70
- <button
71
- type="button"
72
- aria-label="Close"
73
- onClick={() => {
74
- drawerCloseSound.play();
75
- onClose();
76
- }}
77
- className="ring-offset-background focus:ring-ring absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden"
78
- >
79
- <XIcon className="size-4" />
80
- </button>
73
+ {selectedNode ? (
74
+ <>
75
+ {/* Action buttons */}
76
+ {selectedNode.repositoryPath && selectedNode.branch ? (
77
+ <DrawerActions
78
+ repositoryPath={selectedNode.repositoryPath}
79
+ branch={selectedNode.branch}
80
+ specPath={selectedNode.specPath}
81
+ />
82
+ ) : null}
81
83
 
82
- {/* Header */}
83
- <DrawerHeader data-testid="feature-drawer-header">
84
- <DrawerTitle>{selectedNode.name}</DrawerTitle>
85
- <DrawerDescription>{selectedNode.featureId}</DrawerDescription>
86
- </DrawerHeader>
84
+ <Separator />
87
85
 
88
- {/* Action buttons */}
89
- {selectedNode.repositoryPath && selectedNode.branch ? (
90
- <DrawerActions
91
- repositoryPath={selectedNode.repositoryPath}
92
- branch={selectedNode.branch}
93
- specPath={selectedNode.specPath}
94
- />
95
- ) : null}
96
-
97
- <Separator />
98
-
99
- {/* Status */}
100
- <div data-testid="feature-drawer-status" className="flex flex-col gap-3 p-4">
101
- <div className="text-muted-foreground text-xs font-semibold tracking-wider">
102
- {lifecycleDisplayLabels[selectedNode.lifecycle]}
103
- </div>
86
+ {/* Status */}
87
+ <div data-testid="feature-drawer-status" className="flex flex-col gap-3 p-4">
88
+ <div className="text-muted-foreground text-xs font-semibold tracking-wider">
89
+ {lifecycleDisplayLabels[selectedNode.lifecycle]}
90
+ </div>
104
91
 
105
- <StateBadge data={selectedNode} />
92
+ <StateBadge data={selectedNode} />
106
93
 
107
- {selectedNode.progress > 0 ? (
108
- <div data-testid="feature-drawer-progress" className="flex flex-col gap-1">
109
- <div className="text-muted-foreground flex items-center justify-between text-xs">
110
- <span>Progress</span>
111
- <span>{selectedNode.progress}%</span>
112
- </div>
113
- <div className="bg-muted h-2 w-full overflow-hidden rounded-full">
114
- <div
115
- className={cn(
116
- 'h-full rounded-full transition-all',
117
- featureNodeStateConfig[selectedNode.state].progressClass
118
- )}
119
- style={{ width: `${selectedNode.progress}%` }}
120
- />
121
- </div>
94
+ {selectedNode.progress > 0 ? (
95
+ <div data-testid="feature-drawer-progress" className="flex flex-col gap-1">
96
+ <div className="text-muted-foreground flex items-center justify-between text-xs">
97
+ <span>Progress</span>
98
+ <span>{selectedNode.progress}%</span>
122
99
  </div>
123
- ) : null}
124
- </div>
125
-
126
- {/* PR info */}
127
- {selectedNode.pr ? (
128
- <>
129
- <Separator />
130
- <PrInfoSection pr={selectedNode.pr} />
131
- </>
100
+ <div className="bg-muted h-2 w-full overflow-hidden rounded-full">
101
+ <div
102
+ className={cn(
103
+ 'h-full rounded-full transition-all',
104
+ featureNodeStateConfig[selectedNode.state].progressClass
105
+ )}
106
+ style={{ width: `${selectedNode.progress}%` }}
107
+ />
108
+ </div>
109
+ </div>
132
110
  ) : null}
111
+ </div>
133
112
 
134
- <Separator />
113
+ {/* PR info */}
114
+ {selectedNode.pr ? (
115
+ <>
116
+ <Separator />
117
+ <PrInfoSection pr={selectedNode.pr} />
118
+ </>
119
+ ) : null}
135
120
 
136
- {/* Details */}
137
- <DetailsSection data={selectedNode} />
121
+ <Separator />
138
122
 
139
- {/* Delete action */}
140
- {onDelete ? (
141
- <>
142
- <Separator />
143
- <div data-testid="feature-drawer-delete" className="p-4">
144
- <AlertDialog>
145
- <AlertDialogTrigger asChild>
146
- <Button variant="destructive" className="w-full" disabled={isDeleting}>
147
- <Trash2 className="mr-2 h-4 w-4" />
148
- Delete feature
149
- </Button>
150
- </AlertDialogTrigger>
151
- <AlertDialogContent>
152
- <AlertDialogHeader>
153
- <AlertDialogTitle>Delete feature?</AlertDialogTitle>
154
- <AlertDialogDescription>
155
- This will permanently delete <strong>{selectedNode.name}</strong> (
156
- {selectedNode.featureId}). This action cannot be undone.
157
- {selectedNode.state === 'running' ? (
158
- <> This feature has a running agent that will be stopped.</>
159
- ) : null}
160
- </AlertDialogDescription>
161
- </AlertDialogHeader>
162
- <AlertDialogFooter>
163
- <AlertDialogCancel disabled={isDeleting}>Cancel</AlertDialogCancel>
164
- <AlertDialogAction
165
- variant="destructive"
166
- disabled={isDeleting}
167
- onClick={() => onDelete(selectedNode.featureId)}
168
- >
169
- {isDeleting ? (
170
- <>
171
- <Loader2 className="mr-2 h-4 w-4 animate-spin" />
172
- Deleting…
173
- </>
174
- ) : (
175
- 'Delete'
176
- )}
177
- </AlertDialogAction>
178
- </AlertDialogFooter>
179
- </AlertDialogContent>
180
- </AlertDialog>
181
- </div>
182
- </>
183
- ) : null}
184
- </>
185
- ) : null}
186
- </DrawerContent>
187
- </Drawer>
123
+ {/* Details */}
124
+ <DetailsSection data={selectedNode} />
125
+
126
+ {/* Delete action */}
127
+ {onDelete ? (
128
+ <>
129
+ <Separator />
130
+ <div data-testid="feature-drawer-delete" className="p-4">
131
+ <AlertDialog>
132
+ <AlertDialogTrigger asChild>
133
+ <Button variant="destructive" className="w-full" disabled={isDeleting}>
134
+ <Trash2 className="mr-2 h-4 w-4" />
135
+ Delete feature
136
+ </Button>
137
+ </AlertDialogTrigger>
138
+ <AlertDialogContent>
139
+ <AlertDialogHeader>
140
+ <AlertDialogTitle>Delete feature?</AlertDialogTitle>
141
+ <AlertDialogDescription>
142
+ This will permanently delete <strong>{selectedNode.name}</strong> (
143
+ {selectedNode.featureId}). This action cannot be undone.
144
+ {selectedNode.state === 'running' ? (
145
+ <> This feature has a running agent that will be stopped.</>
146
+ ) : null}
147
+ </AlertDialogDescription>
148
+ </AlertDialogHeader>
149
+ <AlertDialogFooter>
150
+ <AlertDialogCancel disabled={isDeleting}>Cancel</AlertDialogCancel>
151
+ <AlertDialogAction
152
+ variant="destructive"
153
+ disabled={isDeleting}
154
+ onClick={() => onDelete(selectedNode.featureId)}
155
+ >
156
+ {isDeleting ? (
157
+ <>
158
+ <Loader2 className="mr-2 h-4 w-4 animate-spin" />
159
+ Deleting…
160
+ </>
161
+ ) : (
162
+ 'Delete'
163
+ )}
164
+ </AlertDialogAction>
165
+ </AlertDialogFooter>
166
+ </AlertDialogContent>
167
+ </AlertDialog>
168
+ </div>
169
+ </>
170
+ ) : null}
171
+ </>
172
+ ) : null}
173
+ </BaseDrawer>
188
174
  );
189
175
  }
190
176
 
@@ -1,2 +1,3 @@
1
1
  export { RepositoryNode } from './repository-node';
2
+ export { RepositoryDrawer, type RepositoryDrawerProps } from './repository-drawer';
2
3
  export type { RepositoryNodeData, RepositoryNodeType } from './repository-node-config';
@@ -0,0 +1,58 @@
1
+ import { useState } from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import { Button } from '@/components/ui/button';
4
+ import { RepositoryDrawer } from './repository-drawer';
5
+ import type { RepositoryNodeData } from './repository-node-config';
6
+
7
+ const meta: Meta<typeof RepositoryDrawer> = {
8
+ title: 'Composed/RepositoryDrawer',
9
+ component: RepositoryDrawer,
10
+ tags: ['autodocs'],
11
+ parameters: {
12
+ layout: 'fullscreen',
13
+ },
14
+ };
15
+
16
+ export default meta;
17
+ type Story = StoryObj<typeof RepositoryDrawer>;
18
+
19
+ const repoData: RepositoryNodeData = {
20
+ id: 'repo-1',
21
+ name: 'shep-ai/cli',
22
+ repositoryPath: '/home/user/shep-ai/cli',
23
+ };
24
+
25
+ function DrawerTrigger({ data, label }: { data: RepositoryNodeData; label: string }) {
26
+ const [selected, setSelected] = useState<RepositoryNodeData | null>(null);
27
+
28
+ return (
29
+ <div className="flex h-screen items-start p-4">
30
+ <Button variant="outline" onClick={() => setSelected(data)}>
31
+ {label}
32
+ </Button>
33
+ <RepositoryDrawer data={selected} onClose={() => setSelected(null)} />
34
+ </div>
35
+ );
36
+ }
37
+
38
+ export const Default: Story = {
39
+ render: () => <DrawerTrigger data={repoData} label="Open Repository" />,
40
+ };
41
+
42
+ export const LongPath: Story = {
43
+ render: () => (
44
+ <DrawerTrigger
45
+ data={{
46
+ ...repoData,
47
+ repositoryPath: '/home/user/projects/company/some-very-long-path/shep-ai/cli',
48
+ }}
49
+ label="Open Long Path"
50
+ />
51
+ ),
52
+ };
53
+
54
+ export const WithoutPath: Story = {
55
+ render: () => (
56
+ <DrawerTrigger data={{ ...repoData, repositoryPath: undefined }} label="Open Without Path" />
57
+ ),
58
+ };
@@ -0,0 +1,82 @@
1
+ 'use client';
2
+
3
+ import { Code2, Terminal, FolderOpen } from 'lucide-react';
4
+ import { BaseDrawer } from '@/components/common/base-drawer';
5
+ import { DrawerTitle, DrawerDescription } from '@/components/ui/drawer';
6
+ import { Separator } from '@/components/ui/separator';
7
+ import { ActionButton } from '@/components/common/action-button';
8
+ import type { RepositoryNodeData } from './repository-node-config';
9
+ import { useRepositoryActions } from './use-repository-actions';
10
+
11
+ export interface RepositoryDrawerProps {
12
+ data: RepositoryNodeData | null;
13
+ onClose: () => void;
14
+ }
15
+
16
+ export function RepositoryDrawer({ data, onClose }: RepositoryDrawerProps) {
17
+ const actions = useRepositoryActions(
18
+ data?.repositoryPath ? { repositoryPath: data.repositoryPath } : null
19
+ );
20
+
21
+ return (
22
+ <BaseDrawer
23
+ open={data !== null}
24
+ onClose={onClose}
25
+ size="sm"
26
+ modal={false}
27
+ data-testid="repository-drawer"
28
+ header={
29
+ data ? (
30
+ <div data-testid="repository-drawer-header">
31
+ <DrawerTitle>{data.name}</DrawerTitle>
32
+ {data.repositoryPath ? (
33
+ <DrawerDescription className="truncate font-mono text-xs">
34
+ {data.repositoryPath}
35
+ </DrawerDescription>
36
+ ) : null}
37
+ </div>
38
+ ) : undefined
39
+ }
40
+ >
41
+ {data?.repositoryPath ? (
42
+ <>
43
+ <Separator />
44
+ <div className="flex flex-col gap-3 p-4">
45
+ <div className="text-muted-foreground text-xs font-semibold tracking-wider">
46
+ OPEN WITH
47
+ </div>
48
+ <div className="flex flex-col gap-2">
49
+ <ActionButton
50
+ label="Open in IDE"
51
+ onClick={actions.openInIde}
52
+ loading={actions.ideLoading}
53
+ error={!!actions.ideError}
54
+ icon={Code2}
55
+ variant="outline"
56
+ size="sm"
57
+ />
58
+ <ActionButton
59
+ label="Open in Shell"
60
+ onClick={actions.openInShell}
61
+ loading={actions.shellLoading}
62
+ error={!!actions.shellError}
63
+ icon={Terminal}
64
+ variant="outline"
65
+ size="sm"
66
+ />
67
+ <ActionButton
68
+ label="Open Folder"
69
+ onClick={actions.openFolder}
70
+ loading={actions.folderLoading}
71
+ error={!!actions.folderError}
72
+ icon={FolderOpen}
73
+ variant="outline"
74
+ size="sm"
75
+ />
76
+ </div>
77
+ </div>
78
+ </>
79
+ ) : null}
80
+ </BaseDrawer>
81
+ );
82
+ }
@@ -1,13 +1,8 @@
1
1
  'use client';
2
2
 
3
- import { Loader2, Trash2, XIcon } from 'lucide-react';
4
- import {
5
- Drawer,
6
- DrawerContent,
7
- DrawerHeader,
8
- DrawerTitle,
9
- DrawerDescription,
10
- } from '@/components/ui/drawer';
3
+ import { Loader2, Trash2 } from 'lucide-react';
4
+ import { BaseDrawer } from '@/components/common/base-drawer';
5
+ import { DrawerTitle, DrawerDescription } from '@/components/ui/drawer';
11
6
  import { Button } from '@/components/ui/button';
12
7
  import { Separator } from '@/components/ui/separator';
13
8
  import {
@@ -42,101 +37,89 @@ export function ReviewDrawerShell({
42
37
  const actions = useFeatureActions(actionsInput);
43
38
 
44
39
  return (
45
- <Drawer
46
- direction="right"
47
- modal={false}
48
- handleOnly
40
+ <BaseDrawer
49
41
  open={open}
50
- onOpenChange={(isOpen) => {
51
- if (!isOpen) onClose();
52
- }}
53
- >
54
- <DrawerContent direction="right" className="w-xl" showCloseButton={false}>
55
- {/* Close button */}
56
- <button
57
- type="button"
58
- aria-label="Close"
59
- onClick={onClose}
60
- className="ring-offset-background focus:ring-ring absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden"
61
- >
62
- <XIcon className="size-4" />
63
- </button>
64
-
65
- {/* Header */}
66
- <DrawerHeader data-testid="feature-drawer-header">
67
- <DrawerTitle>{featureName}</DrawerTitle>
68
- {featureDescription ? (
69
- <DrawerDescription>{featureDescription}</DrawerDescription>
70
- ) : featureId ? (
71
- <DrawerDescription className="sr-only">{featureId}</DrawerDescription>
72
- ) : null}
73
- </DrawerHeader>
42
+ onClose={onClose}
43
+ size="md"
44
+ modal={false}
45
+ data-testid="review-drawer"
46
+ header={
47
+ <>
48
+ <div data-testid="feature-drawer-header">
49
+ <DrawerTitle>{featureName}</DrawerTitle>
50
+ {featureDescription ? (
51
+ <DrawerDescription>{featureDescription}</DrawerDescription>
52
+ ) : featureId ? (
53
+ <DrawerDescription className="sr-only">{featureId}</DrawerDescription>
54
+ ) : null}
55
+ </div>
74
56
 
75
- {/* Action menu + inline delete */}
76
- {actionsInput ? (
77
- <div className="flex items-center gap-2 px-4 pb-3">
78
- <OpenActionMenu
79
- actions={actions}
80
- repositoryPath={actionsInput.repositoryPath}
81
- showSpecs={!!specPath}
82
- />
83
- {onDelete && featureId ? (
84
- <>
85
- <div className="bg-border mx-1 h-4 w-px" />
86
- <AlertDialog>
87
- <AlertDialogTrigger asChild>
88
- <Button
89
- variant="ghost"
90
- size="icon-sm"
91
- aria-label="Delete feature"
92
- disabled={isDeleting}
93
- className="text-muted-foreground hover:text-destructive"
94
- data-testid="review-drawer-delete"
95
- >
96
- {isDeleting ? (
97
- <Loader2 className="size-4 animate-spin" />
98
- ) : (
99
- <Trash2 className="size-4" />
100
- )}
101
- </Button>
102
- </AlertDialogTrigger>
103
- <AlertDialogContent>
104
- <AlertDialogHeader>
105
- <AlertDialogTitle>Delete feature?</AlertDialogTitle>
106
- <AlertDialogDescription>
107
- This will permanently delete <strong>{featureName}</strong> ({featureId}).
108
- This action cannot be undone.
109
- </AlertDialogDescription>
110
- </AlertDialogHeader>
111
- <AlertDialogFooter>
112
- <AlertDialogCancel disabled={isDeleting}>Cancel</AlertDialogCancel>
113
- <AlertDialogAction
114
- variant="destructive"
57
+ {/* Action menu + inline delete */}
58
+ {actionsInput ? (
59
+ <div className="flex items-center gap-2 pt-2">
60
+ <OpenActionMenu
61
+ actions={actions}
62
+ repositoryPath={actionsInput.repositoryPath}
63
+ showSpecs={!!specPath}
64
+ />
65
+ {onDelete && featureId ? (
66
+ <>
67
+ <div className="bg-border mx-1 h-4 w-px" />
68
+ <AlertDialog>
69
+ <AlertDialogTrigger asChild>
70
+ <Button
71
+ variant="ghost"
72
+ size="icon-sm"
73
+ aria-label="Delete feature"
115
74
  disabled={isDeleting}
116
- onClick={() => onDelete(featureId)}
75
+ className="text-muted-foreground hover:text-destructive"
76
+ data-testid="review-drawer-delete"
117
77
  >
118
78
  {isDeleting ? (
119
- <>
120
- <Loader2 className="mr-2 h-4 w-4 animate-spin" />
121
- Deleting…
122
- </>
79
+ <Loader2 className="size-4 animate-spin" />
123
80
  ) : (
124
- 'Delete'
81
+ <Trash2 className="size-4" />
125
82
  )}
126
- </AlertDialogAction>
127
- </AlertDialogFooter>
128
- </AlertDialogContent>
129
- </AlertDialog>
130
- </>
131
- ) : null}
132
- </div>
133
- ) : null}
134
-
135
- <Separator />
83
+ </Button>
84
+ </AlertDialogTrigger>
85
+ <AlertDialogContent>
86
+ <AlertDialogHeader>
87
+ <AlertDialogTitle>Delete feature?</AlertDialogTitle>
88
+ <AlertDialogDescription>
89
+ This will permanently delete <strong>{featureName}</strong> ({featureId}).
90
+ This action cannot be undone.
91
+ </AlertDialogDescription>
92
+ </AlertDialogHeader>
93
+ <AlertDialogFooter>
94
+ <AlertDialogCancel disabled={isDeleting}>Cancel</AlertDialogCancel>
95
+ <AlertDialogAction
96
+ variant="destructive"
97
+ disabled={isDeleting}
98
+ onClick={() => onDelete(featureId)}
99
+ >
100
+ {isDeleting ? (
101
+ <>
102
+ <Loader2 className="mr-2 h-4 w-4 animate-spin" />
103
+ Deleting…
104
+ </>
105
+ ) : (
106
+ 'Delete'
107
+ )}
108
+ </AlertDialogAction>
109
+ </AlertDialogFooter>
110
+ </AlertDialogContent>
111
+ </AlertDialog>
112
+ </>
113
+ ) : null}
114
+ </div>
115
+ ) : null}
116
+ </>
117
+ }
118
+ >
119
+ <Separator />
136
120
 
137
- {/* Content slot */}
138
- <div className="flex min-h-0 flex-1 flex-col">{children}</div>
139
- </DrawerContent>
140
- </Drawer>
121
+ {/* Content slot */}
122
+ <div className="flex min-h-0 flex-1 flex-col">{children}</div>
123
+ </BaseDrawer>
141
124
  );
142
125
  }