@shepai/cli 1.61.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 +11 -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 +145 -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/2960a70537e4fd12.js +0 -10
  197. package/web/.next/static/chunks/45f510f84c7c417d.css +0 -2
  198. package/web/.next/static/chunks/79718b1f5713fdc0.js +0 -1
  199. /package/web/.next/static/{_DBWexyU1QkK02q15qod2 → 23Y-ea-5g4_fUHZyGpCb0}/_buildManifest.js +0 -0
  200. /package/web/.next/static/{_DBWexyU1QkK02q15qod2 → 23Y-ea-5g4_fUHZyGpCb0}/_clientMiddlewareManifest.json +0 -0
  201. /package/web/.next/static/{_DBWexyU1QkK02q15qod2 → 23Y-ea-5g4_fUHZyGpCb0}/_ssgManifest.js +0 -0
@@ -3,7 +3,6 @@
3
3
  import { useState, useCallback, useEffect, useRef } from 'react';
4
4
  import type { LucideIcon } from 'lucide-react';
5
5
  import {
6
- XIcon,
7
6
  PlusIcon,
8
7
  FileIcon,
9
8
  FileTextIcon,
@@ -15,14 +14,8 @@ import {
15
14
  } from 'lucide-react';
16
15
  import { cn } from '@/lib/utils';
17
16
  import { useSoundAction } from '@/hooks/use-sound-action';
18
- import {
19
- Drawer,
20
- DrawerContent,
21
- DrawerHeader,
22
- DrawerTitle,
23
- DrawerDescription,
24
- DrawerFooter,
25
- } from '@/components/ui/drawer';
17
+ import { BaseDrawer } from '@/components/common/base-drawer';
18
+ import { DrawerTitle, DrawerDescription } from '@/components/ui/drawer';
26
19
  import { Button } from '@/components/ui/button';
27
20
  import { Input } from '@/components/ui/input';
28
21
  import { Textarea } from '@/components/ui/textarea';
@@ -142,16 +135,11 @@ export function FeatureCreateDrawer({
142
135
  setParentId(undefined);
143
136
  }, [defaultGates, defaultPush, defaultOpenPr]);
144
137
 
145
- const handleOpenChange = useCallback(
146
- (nextOpen: boolean) => {
147
- if (!nextOpen) {
148
- drawerCloseSound.play();
149
- resetForm();
150
- onClose();
151
- }
152
- },
153
- [onClose, resetForm, drawerCloseSound]
154
- );
138
+ const handleClose = useCallback(() => {
139
+ drawerCloseSound.play();
140
+ resetForm();
141
+ onClose();
142
+ }, [onClose, resetForm, drawerCloseSound]);
155
143
 
156
144
  const handleSubmit = useCallback(
157
145
  (e: React.FormEvent) => {
@@ -173,6 +161,7 @@ export function FeatureCreateDrawer({
173
161
  openPr,
174
162
  ...(parentId ? { parentId } : {}),
175
163
  });
164
+ resetForm();
176
165
  },
177
166
  [
178
167
  name,
@@ -185,6 +174,7 @@ export function FeatureCreateDrawer({
185
174
  openPr,
186
175
  parentId,
187
176
  createSound,
177
+ resetForm,
188
178
  ]
189
179
  );
190
180
 
@@ -206,23 +196,14 @@ export function FeatureCreateDrawer({
206
196
  const hasFeatures = features && features.length > 0;
207
197
 
208
198
  return (
209
- <Drawer direction="right" modal={false} handleOnly open={open} onOpenChange={handleOpenChange}>
210
- <DrawerContent direction="right" className="w-96" showCloseButton={false}>
211
- {/* Close button */}
212
- <button
213
- type="button"
214
- aria-label="Close"
215
- onClick={() => {
216
- drawerCloseSound.play();
217
- onClose();
218
- }}
219
- 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"
220
- >
221
- <XIcon className="size-4" />
222
- </button>
223
-
224
- {/* Header */}
225
- <DrawerHeader>
199
+ <BaseDrawer
200
+ open={open}
201
+ onClose={handleClose}
202
+ size="md"
203
+ modal={false}
204
+ data-testid="feature-create-drawer"
205
+ header={
206
+ <>
226
207
  <div className="flex items-center gap-2">
227
208
  <div className="h-2.5 w-2.5 shrink-0 rounded-full bg-blue-500" />
228
209
  <DrawerTitle>NEW FEATURE</DrawerTitle>
@@ -232,164 +213,156 @@ export function FeatureCreateDrawer({
232
213
  <Badge variant="secondary">Creating...</Badge>
233
214
  </div>
234
215
  </DrawerDescription>
235
- </DrawerHeader>
216
+ </>
217
+ }
218
+ footer={
219
+ <div className="flex flex-row justify-end gap-2">
220
+ <Button variant="outline" onClick={handleClose} disabled={isSubmitting}>
221
+ Cancel
222
+ </Button>
223
+ <Button type="submit" form="create-feature-form" disabled={!name.trim() || isSubmitting}>
224
+ {isSubmitting ? 'Creating...' : '+ Create Feature'}
225
+ </Button>
226
+ </div>
227
+ }
228
+ >
229
+ <Separator />
230
+
231
+ {/* Form body */}
232
+ <div className="p-4">
233
+ <form id="create-feature-form" onSubmit={handleSubmit} className="flex flex-col gap-4">
234
+ {/* Feature name */}
235
+ <div className="flex flex-col gap-1.5">
236
+ <Label
237
+ htmlFor="feature-name"
238
+ className="text-muted-foreground text-xs font-semibold tracking-wider"
239
+ >
240
+ FEATURE NAME
241
+ </Label>
242
+ <Input
243
+ id="feature-name"
244
+ placeholder="e.g. GitHub OAuth Login"
245
+ value={name}
246
+ onChange={(e) => setName(e.target.value)}
247
+ required
248
+ disabled={isSubmitting}
249
+ />
250
+ </div>
236
251
 
237
- <Separator />
252
+ {/* Description */}
253
+ <div className="flex flex-col gap-1.5">
254
+ <Label
255
+ htmlFor="feature-description"
256
+ className="text-muted-foreground text-xs font-semibold tracking-wider"
257
+ >
258
+ DESCRIPTION
259
+ </Label>
260
+ <Textarea
261
+ id="feature-description"
262
+ placeholder="Describe what this feature does..."
263
+ value={description}
264
+ onChange={(e) => setDescription(e.target.value)}
265
+ rows={4}
266
+ disabled={isSubmitting}
267
+ />
268
+ </div>
238
269
 
239
- {/* Form body */}
240
- <div className="flex-1 overflow-y-auto p-4">
241
- <form id="create-feature-form" onSubmit={handleSubmit} className="flex flex-col gap-4">
242
- {/* Feature name */}
270
+ {/* Parent feature selector (only when features are available) */}
271
+ {hasFeatures ? (
243
272
  <div className="flex flex-col gap-1.5">
244
273
  <Label
245
- htmlFor="feature-name"
274
+ htmlFor="parent-feature"
246
275
  className="text-muted-foreground text-xs font-semibold tracking-wider"
247
276
  >
248
- FEATURE NAME
277
+ PARENT FEATURE
249
278
  </Label>
250
- <Input
251
- id="feature-name"
252
- placeholder="e.g. GitHub OAuth Login"
253
- value={name}
254
- onChange={(e) => setName(e.target.value)}
255
- required
279
+ <ParentFeatureCombobox
280
+ features={features}
281
+ value={parentId}
282
+ onChange={setParentId}
256
283
  disabled={isSubmitting}
257
284
  />
258
285
  </div>
286
+ ) : null}
287
+
288
+ {/* Auto-approve checkboxes */}
289
+ <div className="flex flex-col gap-1.5">
290
+ <Label className="text-muted-foreground text-xs font-semibold tracking-wider">
291
+ APPROVE
292
+ </Label>
293
+ <CheckboxGroup
294
+ label="Autonomous Mode"
295
+ description="YOLO!"
296
+ parentAriaLabel="Auto approve all"
297
+ options={AUTO_APPROVE_OPTIONS}
298
+ value={approvalGates}
299
+ onValueChange={setApprovalGates}
300
+ disabled={isSubmitting}
301
+ />
302
+ </div>
259
303
 
260
- {/* Description */}
261
- <div className="flex flex-col gap-1.5">
262
- <Label
263
- htmlFor="feature-description"
264
- className="text-muted-foreground text-xs font-semibold tracking-wider"
265
- >
266
- DESCRIPTION
267
- </Label>
268
- <Textarea
269
- id="feature-description"
270
- placeholder="Describe what this feature does..."
271
- value={description}
272
- onChange={(e) => setDescription(e.target.value)}
273
- rows={4}
304
+ {/* Git options */}
305
+ <div className="flex flex-col gap-1.5">
306
+ <Label className="text-muted-foreground text-xs font-semibold tracking-wider">
307
+ GIT
308
+ </Label>
309
+ <div className="flex flex-col gap-2">
310
+ <CheckboxGroupItem
311
+ id="push"
312
+ label="Push"
313
+ description="Push branch to remote after implementation."
314
+ checked={push || openPr}
315
+ onCheckedChange={setPush}
316
+ disabled={openPr || isSubmitting}
317
+ />
318
+ <CheckboxGroupItem
319
+ id="open-pr"
320
+ label="Create PR"
321
+ description="Open a pull request after pushing."
322
+ checked={openPr}
323
+ onCheckedChange={setOpenPr}
274
324
  disabled={isSubmitting}
275
325
  />
276
326
  </div>
327
+ </div>
277
328
 
278
- {/* Parent feature selector (only when features are available) */}
279
- {hasFeatures ? (
280
- <div className="flex flex-col gap-1.5">
281
- <Label
282
- htmlFor="parent-feature"
283
- className="text-muted-foreground text-xs font-semibold tracking-wider"
284
- >
285
- PARENT FEATURE
286
- </Label>
287
- <ParentFeatureCombobox
288
- features={features}
289
- value={parentId}
290
- onChange={setParentId}
291
- disabled={isSubmitting}
292
- />
293
- </div>
294
- ) : null}
295
-
296
- {/* Auto-approve checkboxes */}
297
- <div className="flex flex-col gap-1.5">
329
+ {/* Attachments */}
330
+ <div className="flex flex-col gap-2">
331
+ <div className="flex items-center justify-between">
298
332
  <Label className="text-muted-foreground text-xs font-semibold tracking-wider">
299
- APPROVE
333
+ ATTACHMENTS
334
+ {attachments.length > 0 && (
335
+ <span className="text-muted-foreground/60 ml-1.5">({attachments.length})</span>
336
+ )}
300
337
  </Label>
301
- <CheckboxGroup
302
- label="Autonomous Mode"
303
- description="YOLO!"
304
- parentAriaLabel="Auto approve all"
305
- options={AUTO_APPROVE_OPTIONS}
306
- value={approvalGates}
307
- onValueChange={setApprovalGates}
338
+ <Button
339
+ type="button"
340
+ variant="outline"
341
+ size="xs"
342
+ onClick={handleAddFiles}
308
343
  disabled={isSubmitting}
309
- />
344
+ >
345
+ <PlusIcon className="size-3" />
346
+ Add Files
347
+ </Button>
310
348
  </div>
311
349
 
312
- {/* Git options */}
313
- <div className="flex flex-col gap-1.5">
314
- <Label className="text-muted-foreground text-xs font-semibold tracking-wider">
315
- GIT
316
- </Label>
350
+ {attachments.length > 0 && (
317
351
  <div className="flex flex-col gap-2">
318
- <CheckboxGroupItem
319
- id="push"
320
- label="Push"
321
- description="Push branch to remote after implementation."
322
- checked={push || openPr}
323
- onCheckedChange={setPush}
324
- disabled={openPr || isSubmitting}
325
- />
326
- <CheckboxGroupItem
327
- id="open-pr"
328
- label="Create PR"
329
- description="Open a pull request after pushing."
330
- checked={openPr}
331
- onCheckedChange={setOpenPr}
332
- disabled={isSubmitting}
333
- />
352
+ {attachments.map((file) => (
353
+ <AttachmentCard
354
+ key={file.path}
355
+ file={file}
356
+ onRemove={() => handleRemoveFile(file.path)}
357
+ disabled={isSubmitting}
358
+ />
359
+ ))}
334
360
  </div>
335
- </div>
336
-
337
- {/* Attachments */}
338
- <div className="flex flex-col gap-2">
339
- <div className="flex items-center justify-between">
340
- <Label className="text-muted-foreground text-xs font-semibold tracking-wider">
341
- ATTACHMENTS
342
- {attachments.length > 0 && (
343
- <span className="text-muted-foreground/60 ml-1.5">({attachments.length})</span>
344
- )}
345
- </Label>
346
- <Button
347
- type="button"
348
- variant="outline"
349
- size="xs"
350
- onClick={handleAddFiles}
351
- disabled={isSubmitting}
352
- >
353
- <PlusIcon className="size-3" />
354
- Add Files
355
- </Button>
356
- </div>
357
-
358
- {attachments.length > 0 && (
359
- <div className="flex flex-col gap-2">
360
- {attachments.map((file) => (
361
- <AttachmentCard
362
- key={file.path}
363
- file={file}
364
- onRemove={() => handleRemoveFile(file.path)}
365
- disabled={isSubmitting}
366
- />
367
- ))}
368
- </div>
369
- )}
370
- </div>
371
- </form>
372
- </div>
373
-
374
- {/* Footer */}
375
- <Separator />
376
- <DrawerFooter className="flex-row justify-end gap-2">
377
- <Button
378
- variant="outline"
379
- onClick={() => {
380
- drawerCloseSound.play();
381
- onClose();
382
- }}
383
- disabled={isSubmitting}
384
- >
385
- Cancel
386
- </Button>
387
- <Button type="submit" form="create-feature-form" disabled={!name.trim() || isSubmitting}>
388
- {isSubmitting ? 'Creating...' : '+ Create Feature'}
389
- </Button>
390
- </DrawerFooter>
391
- </DrawerContent>
392
- </Drawer>
361
+ )}
362
+ </div>
363
+ </form>
364
+ </div>
365
+ </BaseDrawer>
393
366
  );
394
367
  }
395
368
 
@@ -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