@shepai/cli 1.31.0 → 1.32.1

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 (89) hide show
  1. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.d.ts +13 -2
  2. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.d.ts.map +1 -1
  3. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.js +4 -13
  4. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.d.ts +4 -10
  5. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.d.ts.map +1 -1
  6. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.js +7 -19
  7. package/dist/src/presentation/web/components/common/feature-create-drawer/index.d.ts +1 -1
  8. package/dist/src/presentation/web/components/common/feature-create-drawer/index.d.ts.map +1 -1
  9. package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.stories.d.ts.map +1 -1
  10. package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.stories.js +11 -0
  11. package/dist/src/presentation/web/components/common/feature-node/feature-node-state-config.d.ts +1 -1
  12. package/dist/src/presentation/web/components/common/feature-node/feature-node-state-config.d.ts.map +1 -1
  13. package/dist/src/presentation/web/components/common/feature-node/feature-node-state-config.js +10 -0
  14. package/dist/src/presentation/web/components/common/feature-node/feature-node.d.ts.map +1 -1
  15. package/dist/src/presentation/web/components/common/feature-node/feature-node.js +4 -2
  16. package/dist/src/presentation/web/components/common/feature-node/feature-node.stories.d.ts +1 -0
  17. package/dist/src/presentation/web/components/common/feature-node/feature-node.stories.d.ts.map +1 -1
  18. package/dist/src/presentation/web/components/common/feature-node/feature-node.stories.js +21 -0
  19. package/dist/src/presentation/web/components/common/index.d.ts +1 -1
  20. package/dist/src/presentation/web/components/common/index.d.ts.map +1 -1
  21. package/dist/src/presentation/web/components/common/index.js +1 -1
  22. package/dist/src/presentation/web/components/features/control-center/control-center-inner.d.ts.map +1 -1
  23. package/dist/src/presentation/web/components/features/control-center/control-center-inner.js +2 -2
  24. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts +3 -3
  25. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts.map +1 -1
  26. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.js +76 -23
  27. package/dist/src/presentation/web/components/features/features-canvas/features-canvas.d.ts.map +1 -1
  28. package/dist/src/presentation/web/components/features/features-canvas/features-canvas.js +2 -1
  29. package/dist/tsconfig.build.tsbuildinfo +1 -1
  30. package/package.json +2 -2
  31. package/web/.next/BUILD_ID +1 -1
  32. package/web/.next/build-manifest.json +2 -2
  33. package/web/.next/cache/.previewinfo +1 -1
  34. package/web/.next/cache/.rscinfo +1 -1
  35. package/web/.next/cache/.tsbuildinfo +1 -1
  36. package/web/.next/cache/config.json +3 -3
  37. package/web/.next/fallback-build-manifest.json +2 -2
  38. package/web/.next/prerender-manifest.json +3 -3
  39. package/web/.next/required-server-files.js +1 -1
  40. package/web/.next/required-server-files.json +1 -1
  41. package/web/.next/server/app/_global-error.html +2 -2
  42. package/web/.next/server/app/_global-error.rsc +1 -1
  43. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  44. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  45. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  46. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  47. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  48. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  49. package/web/.next/server/app/_not-found.html +2 -2
  50. package/web/.next/server/app/_not-found.rsc +2 -2
  51. package/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  52. package/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  53. package/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  54. package/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  55. package/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  56. package/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  57. package/web/.next/server/app/page_client-reference-manifest.js +1 -1
  58. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  59. package/web/.next/server/app/version.html +2 -2
  60. package/web/.next/server/app/version.rsc +2 -2
  61. package/web/.next/server/app/version.segments/_full.segment.rsc +2 -2
  62. package/web/.next/server/app/version.segments/_head.segment.rsc +1 -1
  63. package/web/.next/server/app/version.segments/_index.segment.rsc +2 -2
  64. package/web/.next/server/app/version.segments/_tree.segment.rsc +2 -2
  65. package/web/.next/server/app/version.segments/version/__PAGE__.segment.rsc +1 -1
  66. package/web/.next/server/app/version.segments/version.segment.rsc +1 -1
  67. package/web/.next/server/chunks/ssr/[root-of-the-server]__f12b60be._.js +1 -1
  68. package/web/.next/server/chunks/ssr/_09afa42a._.js +2 -7
  69. package/web/.next/server/chunks/ssr/_09afa42a._.js.map +1 -1
  70. package/web/.next/server/pages/404.html +2 -2
  71. package/web/.next/server/pages/500.html +2 -2
  72. package/web/.next/server/server-reference-manifest.js +1 -1
  73. package/web/.next/server/server-reference-manifest.json +1 -1
  74. package/web/.next/static/chunks/{daacb9d30e4fc428.js → 48b2cea957bfe6ab.js} +2 -7
  75. package/web/.next/static/chunks/{fd816afd85fd9a19.css → b98242e5ef2a1bd3.css} +1 -1
  76. package/web/.next/trace +1 -1
  77. package/web/.next/trace-build +1 -1
  78. package/web/public/.gitkeep +0 -0
  79. package/web/public/favicon-dark.svg +6 -0
  80. package/web/public/favicon-light.svg +6 -0
  81. package/web/public/icons/agents/aider.png +0 -0
  82. package/web/public/icons/agents/claude-ai-icon.svg +1 -0
  83. package/web/public/icons/agents/claude-code.jpeg +0 -0
  84. package/web/public/icons/agents/continue.jpeg +0 -0
  85. package/web/public/icons/agents/cursor.jpeg +0 -0
  86. package/web/public/icons/agents/gemini-cli.jpeg +0 -0
  87. /package/web/.next/static/{zHWQYn5H7ZmzuYWmx7b2E → h8rPYdBm1U3Fb0NMh7yR0}/_buildManifest.js +0 -0
  88. /package/web/.next/static/{zHWQYn5H7ZmzuYWmx7b2E → h8rPYdBm1U3Fb0NMh7yR0}/_clientMiddlewareManifest.json +0 -0
  89. /package/web/.next/static/{zHWQYn5H7ZmzuYWmx7b2E → h8rPYdBm1U3Fb0NMh7yR0}/_ssgManifest.js +0 -0
@@ -1,9 +1,20 @@
1
- import type { CreateFeatureInput } from '../../../../../../packages/core/src/application/use-cases/features/create/types.js';
1
+ import type { FileAttachment } from '../../../../../../packages/core/src/infrastructure/services/file-dialog.service.js';
2
2
  export type { FileAttachment } from '../../../../../../packages/core/src/infrastructure/services/file-dialog.service.js';
3
+ export interface FeatureCreatePayload {
4
+ name: string;
5
+ description?: string;
6
+ attachments: FileAttachment[];
7
+ repositoryPath: string;
8
+ approvalGates: {
9
+ allowPrd: boolean;
10
+ allowPlan: boolean;
11
+ allowMerge: boolean;
12
+ };
13
+ }
3
14
  export interface FeatureCreateDrawerProps {
4
15
  open: boolean;
5
16
  onClose: () => void;
6
- onSubmit: (data: CreateFeatureInput) => void;
17
+ onSubmit: (data: FeatureCreatePayload) => void;
7
18
  repositoryPath: string;
8
19
  isSubmitting?: boolean;
9
20
  }
@@ -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":"AA8BA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0DAA0D,CAAC;AAGnG,YAAY,EAAE,cAAc,EAAE,MAAM,0DAA0D,CAAC;AAiC/F,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,CAAC,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAC7C,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,cAAc,EACd,YAAoB,GACrB,EAAE,wBAAwB,2CA6L1B"}
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":"AA6BA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0DAA0D,CAAC;AAG/F,YAAY,EAAE,cAAc,EAAE,MAAM,0DAA0D,CAAC;AAE/F,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,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;CACH;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;CACxB;AAED,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,cAAc,EACd,YAAoB,GACrB,EAAE,wBAAwB,2CA+L1B"}
@@ -22,17 +22,6 @@ const EMPTY_GATES = {
22
22
  allowPlan: false,
23
23
  allowMerge: false,
24
24
  };
25
- function composeUserInput(name, description, attachments) {
26
- let userInput = `Feature: ${name}`;
27
- if (description) {
28
- userInput += `\n\n${description}`;
29
- }
30
- if (attachments.length > 0) {
31
- const paths = attachments.map((a) => `- ${a.path}`).join('\n');
32
- userInput += `\n\nAttached files:\n${paths}`;
33
- }
34
- return userInput;
35
- }
36
25
  export function FeatureCreateDrawer({ open, onClose, onSubmit, repositoryPath, isSubmitting = false, }) {
37
26
  const [name, setName] = useState('');
38
27
  const [description, setDescription] = useState('');
@@ -54,9 +43,11 @@ export function FeatureCreateDrawer({ open, onClose, onSubmit, repositoryPath, i
54
43
  e.preventDefault();
55
44
  if (!name.trim())
56
45
  return;
57
- const userInput = composeUserInput(name.trim(), description.trim() || undefined, attachments);
46
+ const trimmedDescription = description.trim() || undefined;
58
47
  onSubmit({
59
- userInput,
48
+ name: name.trim(),
49
+ description: trimmedDescription,
50
+ attachments,
60
51
  repositoryPath,
61
52
  approvalGates: {
62
53
  allowPrd: approvalGates.allowPrd ?? false,
@@ -18,13 +18,12 @@ import { FeatureCreateDrawer } from './feature-create-drawer.js';
18
18
  * |------|------|-------------|
19
19
  * | `open` | `boolean` | Controls drawer visibility |
20
20
  * | `onClose` | `() => void` | Called on dismiss (close button, cancel, or backdrop) |
21
- * | `onSubmit` | `(data: CreateFeatureInput) => void` | Called with `{ userInput, repositoryPath, approvalGates }` |
21
+ * | `onSubmit` | `(data: FeatureCreatePayload) => void` | Called with `{ name, description, attachments, repositoryPath, approvalGates }` |
22
22
  * | `repositoryPath` | `string` | Repository path (mandatory) included in the submitted data |
23
- * | `isSubmitting` | `boolean` | Disables all fields and shows "Creating..." on submit button |
24
23
  *
25
24
  * ### Behavior
26
25
  * - Form resets (name, description, attachments, checkboxes) when the drawer closes
27
- * - Submit button is disabled when name is empty OR `isSubmitting` is true
26
+ * - Submit button is disabled when name is empty
28
27
  * - `approvalGates` always included: `{ allowPrd, allowPlan, allowMerge }` (all false by default)
29
28
  * - Non-modal (`modal={false}`) — canvas stays interactive behind the drawer
30
29
  * - Fixed width: 384px (`w-96`)
@@ -41,11 +40,6 @@ export declare const Default: Story;
41
40
  * Demonstrates the form in a "ready to submit" state.
42
41
  */
43
42
  export declare const PreFilled: Story;
44
- /**
45
- * Submitting/loading state — all form fields are disabled, submit button shows "Creating...".
46
- * Click the trigger to open and observe the disabled form.
47
- */
48
- export declare const Submitting: Story;
49
43
  /**
50
44
  * Validation state — open the drawer with an empty name field.
51
45
  * The "+ Create Feature" button is disabled because name validation fails.
@@ -77,8 +71,8 @@ export declare const MergeOnly: Story;
77
71
  * Fully interactive story — open the drawer, fill the form, toggle checkboxes,
78
72
  * and click "Add Files" to attach files via the native OS file picker.
79
73
  *
80
- * **Submitted data** is displayed in a styled panel showing `CreateFeatureInput`
81
- * with `approvalGates: { allowPrd, allowPlan, allowMerge }`.
74
+ * **Submitted data** is displayed in a styled panel showing `FeatureCreatePayload`
75
+ * with `{ name, description, attachments, repositoryPath, approvalGates }`.
82
76
  *
83
77
  * In Storybook, the native picker won't work (no backend), but submitted data
84
78
  * would show the paths if files were attached programmatically.
@@ -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;AAQ9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,mBAAmB,CA8B1C,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,UAAU,EAAE,KAExB,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;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,EAAE,KAsCzB,CAAC"}
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;AAQ9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,mBAAmB,CAyB1C,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,mBAAmB,CAAC,CAAC;AA0ClD,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;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,EAAE,KAsCzB,CAAC"}
@@ -24,13 +24,12 @@ import { Button } from '../../ui/button.js';
24
24
  * |------|------|-------------|
25
25
  * | `open` | `boolean` | Controls drawer visibility |
26
26
  * | `onClose` | `() => void` | Called on dismiss (close button, cancel, or backdrop) |
27
- * | `onSubmit` | `(data: CreateFeatureInput) => void` | Called with `{ userInput, repositoryPath, approvalGates }` |
27
+ * | `onSubmit` | `(data: FeatureCreatePayload) => void` | Called with `{ name, description, attachments, repositoryPath, approvalGates }` |
28
28
  * | `repositoryPath` | `string` | Repository path (mandatory) included in the submitted data |
29
- * | `isSubmitting` | `boolean` | Disables all fields and shows "Creating..." on submit button |
30
29
  *
31
30
  * ### Behavior
32
31
  * - Form resets (name, description, attachments, checkboxes) when the drawer closes
33
- * - Submit button is disabled when name is empty OR `isSubmitting` is true
32
+ * - Submit button is disabled when name 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: 384px (`w-96`)
@@ -53,16 +52,12 @@ const meta = {
53
52
  description: 'Callback fired when the drawer is dismissed (close button, cancel, or backdrop click).',
54
53
  },
55
54
  onSubmit: {
56
- description: 'Callback fired with `CreateFeatureInput` when the form is submitted. Receives `{ userInput, repositoryPath, approvalGates }`.',
55
+ description: 'Callback fired with `FeatureCreatePayload` when the form is submitted. Receives `{ name, description, attachments, repositoryPath, approvalGates }`.',
57
56
  },
58
57
  repositoryPath: {
59
58
  control: 'text',
60
59
  description: 'Repository path (mandatory) included in the submitted data.',
61
60
  },
62
- isSubmitting: {
63
- control: 'boolean',
64
- description: 'When true, all form fields and buttons are disabled, and the submit button shows "Creating...".',
65
- },
66
61
  },
67
62
  };
68
63
  export default meta;
@@ -75,7 +70,7 @@ const logClose = fn().mockName('onClose');
75
70
  * Trigger wrapper — every story uses this so nothing auto-opens on Docs page
76
71
  * ------------------------------------------------------------------------- */
77
72
  /** Starts closed — click button to open. Actions are logged. */
78
- function CreateDrawerTrigger({ isSubmitting = false, label = 'Open Create Feature', }) {
73
+ function CreateDrawerTrigger({ label = 'Open Create Feature' }) {
79
74
  const [open, setOpen] = useState(false);
80
75
  return (_jsxs("div", { className: "flex h-screen items-start p-4", children: [_jsx(Button, { variant: "outline", onClick: () => setOpen(true), children: label }), _jsx(FeatureCreateDrawer, { open: open, onClose: () => {
81
76
  setOpen(false);
@@ -83,7 +78,7 @@ function CreateDrawerTrigger({ isSubmitting = false, label = 'Open Create Featur
83
78
  }, onSubmit: (data) => {
84
79
  logSubmit(data);
85
80
  setOpen(false);
86
- }, repositoryPath: "/Users/dev/my-repo", isSubmitting: isSubmitting })] }));
81
+ }, repositoryPath: "/Users/dev/my-repo" })] }));
87
82
  }
88
83
  /* ---------------------------------------------------------------------------
89
84
  * Per-state stories
@@ -109,13 +104,6 @@ export const PreFilled = {
109
104
  await userEvent.type(descInput, 'Implement OAuth2 authentication with GitHub as the identity provider. Includes login, callback handling, and token refresh.');
110
105
  },
111
106
  };
112
- /**
113
- * Submitting/loading state — all form fields are disabled, submit button shows "Creating...".
114
- * Click the trigger to open and observe the disabled form.
115
- */
116
- export const Submitting = {
117
- render: () => _jsx(CreateDrawerTrigger, { isSubmitting: true, label: "Open (Submitting)" }),
118
- };
119
107
  /**
120
108
  * Validation state — open the drawer with an empty name field.
121
109
  * The "+ Create Feature" button is disabled because name validation fails.
@@ -198,8 +186,8 @@ export const MergeOnly = {
198
186
  * Fully interactive story — open the drawer, fill the form, toggle checkboxes,
199
187
  * and click "Add Files" to attach files via the native OS file picker.
200
188
  *
201
- * **Submitted data** is displayed in a styled panel showing `CreateFeatureInput`
202
- * with `approvalGates: { allowPrd, allowPlan, allowMerge }`.
189
+ * **Submitted data** is displayed in a styled panel showing `FeatureCreatePayload`
190
+ * with `{ name, description, attachments, repositoryPath, approvalGates }`.
203
191
  *
204
192
  * In Storybook, the native picker won't work (no backend), but submitted data
205
193
  * would show the paths if files were attached programmatically.
@@ -1,2 +1,2 @@
1
- export { FeatureCreateDrawer, type FeatureCreateDrawerProps, type FileAttachment, } from './feature-create-drawer.js';
1
+ export { FeatureCreateDrawer, type FeatureCreateDrawerProps, type FeatureCreatePayload, type FileAttachment, } from './feature-create-drawer.js';
2
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-create-drawer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,KAAK,wBAAwB,EAC7B,KAAK,cAAc,GACpB,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-create-drawer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,KAAK,cAAc,GACpB,MAAM,yBAAyB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"feature-drawer.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer/feature-drawer.stories.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAKjD,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,aAAa,CAOpC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,aAAa,CAAC,CAAC;AAgG5C,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,KAE5B,CAAC;AAEF,eAAO,MAAM,IAAI,EAAE,KAElB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAEF,eAAO,MAAM,KAAK,EAAE,KAEnB,CAAC;AAMF,wEAAwE;AACxE,eAAO,MAAM,WAAW,EAAE,KAEzB,CAAC;AAEF,qFAAqF;AACrF,eAAO,MAAM,SAAS,EAAE,KAqBvB,CAAC;AAuDF,0DAA0D;AAC1D,eAAO,MAAM,SAAS,EAAE,KAEvB,CAAC;AA6CF,wDAAwD;AACxD,eAAO,MAAM,aAAa,EAAE,KAE3B,CAAC;AAMF,qDAAqD;AACrD,eAAO,MAAM,gBAAgB,EAAE,KAO9B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,KAE/B,CAAC;AAoCF,wDAAwD;AACxD,eAAO,MAAM,gBAAgB,EAAE,KAE9B,CAAC;AAEF,yEAAyE;AACzE,eAAO,MAAM,aAAa,EAAE,KAE3B,CAAC;AAEF,+FAA+F;AAC/F,eAAO,MAAM,kBAAkB,EAAE,KAEhC,CAAC"}
1
+ {"version":3,"file":"feature-drawer.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer/feature-drawer.stories.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAKjD,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,aAAa,CAOpC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,aAAa,CAAC,CAAC;AAgG5C,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,KAE5B,CAAC;AAEF,eAAO,MAAM,IAAI,EAAE,KAElB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAEF,eAAO,MAAM,KAAK,EAAE,KAEnB,CAAC;AAMF,wEAAwE;AACxE,eAAO,MAAM,WAAW,EAAE,KAEzB,CAAC;AAEF,qFAAqF;AACrF,eAAO,MAAM,SAAS,EAAE,KAqBvB,CAAC;AAmEF,0DAA0D;AAC1D,eAAO,MAAM,SAAS,EAAE,KAEvB,CAAC;AA6CF,wDAAwD;AACxD,eAAO,MAAM,aAAa,EAAE,KAE3B,CAAC;AAMF,qDAAqD;AACrD,eAAO,MAAM,gBAAgB,EAAE,KAO9B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,KAE/B,CAAC;AAoCF,wDAAwD;AACxD,eAAO,MAAM,gBAAgB,EAAE,KAE9B,CAAC;AAEF,yEAAyE;AACzE,eAAO,MAAM,aAAa,EAAE,KAE3B,CAAC;AAEF,+FAA+F;AAC/F,eAAO,MAAM,kBAAkB,EAAE,KAEhC,CAAC"}
@@ -129,7 +129,18 @@ export const AllFields = {
129
129
  /* ---------------------------------------------------------------------------
130
130
  * Matrix stories — interactive state/lifecycle switcher
131
131
  * ------------------------------------------------------------------------- */
132
+ const creatingData = {
133
+ name: 'User Onboarding',
134
+ description: 'Implement guided onboarding wizard',
135
+ featureId: '',
136
+ lifecycle: 'requirements',
137
+ state: 'creating',
138
+ progress: 0,
139
+ repositoryPath: '/home/user/my-repo',
140
+ branch: '',
141
+ };
132
142
  const stateFixtures = {
143
+ creating: creatingData,
133
144
  running: runningData,
134
145
  'action-required': actionRequiredData,
135
146
  done: doneData,
@@ -1,7 +1,7 @@
1
1
  import { type LucideIcon } from 'lucide-react';
2
2
  import type { Node } from '@xyflow/react';
3
3
  import type { AgentTypeValue } from './agent-type-icons.js';
4
- export type FeatureNodeState = 'running' | 'action-required' | 'done' | 'blocked' | 'error';
4
+ export type FeatureNodeState = 'creating' | 'running' | 'action-required' | 'done' | 'blocked' | 'error';
5
5
  export type FeatureLifecyclePhase = 'requirements' | 'research' | 'implementation' | 'review' | 'deploy' | 'maintain';
6
6
  /** Human-readable display labels for lifecycle phases. */
7
7
  export declare const lifecycleDisplayLabels: Record<FeatureLifecyclePhase, string>;
@@ -1 +1 @@
1
- {"version":3,"file":"feature-node-state-config.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-node-state-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmD,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAChG,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,iBAAiB,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;AAE5F,MAAM,MAAM,qBAAqB,GAC7B,cAAc,GACd,UAAU,GACV,gBAAgB,GAChB,QAAQ,GACR,QAAQ,GACR,UAAU,CAAC;AAEf,0DAA0D;AAC1D,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,qBAAqB,EAAE,MAAM,CAOxE,CAAC;AAEF,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,qBAAqB,EAAE,MAAM,CAOvE,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,qBAAqB,CAAC;IACjC,KAAK,EAAE,gBAAgB,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,cAAc,EAAE,MAAM,CAAC;IACvB,uCAAuC;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,eAAe,EAAE,aAAa,CAAC,CAAC;AAEnE,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,UAAU,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,gBAAgB,EAAE,sBAAsB,CAmDnF,CAAC"}
1
+ {"version":3,"file":"feature-node-state-config.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-node-state-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmD,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAChG,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD,MAAM,MAAM,gBAAgB,GACxB,UAAU,GACV,SAAS,GACT,iBAAiB,GACjB,MAAM,GACN,SAAS,GACT,OAAO,CAAC;AAEZ,MAAM,MAAM,qBAAqB,GAC7B,cAAc,GACd,UAAU,GACV,gBAAgB,GAChB,QAAQ,GACR,QAAQ,GACR,UAAU,CAAC;AAEf,0DAA0D;AAC1D,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,qBAAqB,EAAE,MAAM,CAOxE,CAAC;AAEF,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,qBAAqB,EAAE,MAAM,CAOvE,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,qBAAqB,CAAC;IACjC,KAAK,EAAE,gBAAgB,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,cAAc,EAAE,MAAM,CAAC;IACvB,uCAAuC;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,eAAe,EAAE,aAAa,CAAC,CAAC;AAEnE,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,UAAU,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,gBAAgB,EAAE,sBAAsB,CA6DnF,CAAC"}
@@ -18,6 +18,16 @@ export const lifecycleRunningVerbs = {
18
18
  maintain: 'Maintaining',
19
19
  };
20
20
  export const featureNodeStateConfig = {
21
+ creating: {
22
+ icon: Loader2,
23
+ borderClass: 'border-l-blue-500',
24
+ labelClass: 'text-blue-500',
25
+ progressClass: 'bg-blue-500',
26
+ badgeClass: 'text-blue-700',
27
+ badgeBgClass: 'bg-blue-50',
28
+ label: 'Creating...',
29
+ showProgressBar: false,
30
+ },
21
31
  running: {
22
32
  icon: Loader2,
23
33
  borderClass: 'border-l-blue-500',
@@ -1 +1 @@
1
- {"version":3,"file":"feature-node.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-node.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAwBnE,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,QAAQ,GACT,EAAE;IACD,IAAI,EAAE,eAAe,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,2CAqJA"}
1
+ {"version":3,"file":"feature-node.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-node.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AA0BnE,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,QAAQ,GACT,EAAE;IACD,IAAI,EAAE,eAAe,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,2CAsKA"}
@@ -12,6 +12,8 @@ function AgentIcon({ agentType, className }) {
12
12
  function getBadgeText(data) {
13
13
  const config = featureNodeStateConfig[data.state];
14
14
  switch (data.state) {
15
+ case 'creating':
16
+ return 'Creating...';
15
17
  case 'running':
16
18
  return lifecycleRunningVerbs[data.lifecycle];
17
19
  case 'done':
@@ -27,10 +29,10 @@ function getBadgeText(data) {
27
29
  export function FeatureNode({ data, selected, }) {
28
30
  const config = featureNodeStateConfig[data.state];
29
31
  const Icon = config.icon;
30
- return (_jsxs("div", { className: "group relative", children: [data.showHandles ? (_jsx(Handle, { type: "target", position: Position.Left, isConnectable: false, className: "opacity-0!" })) : null, _jsxs("div", { "data-testid": "feature-node-card", className: cn('bg-card flex min-h-35 w-72 flex-col rounded-lg border p-3 shadow-sm', selected && 'ring-primary ring-2'), children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { "data-testid": "feature-node-lifecycle-label", className: cn('text-[10px] font-semibold tracking-wider'), children: lifecycleDisplayLabels[data.lifecycle] }), data.onSettings ? (_jsx("button", { type: "button", "aria-label": "Settings", "data-testid": "feature-node-settings-button", onClick: (e) => {
32
+ return (_jsxs("div", { className: "group relative", children: [data.showHandles ? (_jsx(Handle, { type: "target", position: Position.Left, isConnectable: false, className: "opacity-0!" })) : null, _jsxs("div", { "data-testid": "feature-node-card", "aria-busy": data.state === 'creating' ? 'true' : undefined, className: cn('bg-card flex min-h-35 w-72 flex-col rounded-lg border p-3 shadow-sm', selected && 'ring-primary ring-2'), children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { "data-testid": "feature-node-lifecycle-label", className: cn('text-[10px] font-semibold tracking-wider'), children: lifecycleDisplayLabels[data.lifecycle] }), data.onSettings ? (_jsx("button", { type: "button", "aria-label": "Settings", "data-testid": "feature-node-settings-button", onClick: (e) => {
31
33
  e.stopPropagation();
32
34
  data.onSettings?.();
33
- }, className: "nodrag text-muted-foreground hover:text-foreground -mt-1 -mr-1 p-1", children: _jsx(Settings, { className: "h-3.5 w-3.5" }) })) : null] }), _jsx("h3", { className: "mt-1 truncate text-sm font-bold", children: data.name }), data.description ? (_jsx("p", { "data-testid": "feature-node-description", className: "text-muted-foreground mt-0.5 truncate text-xs", children: data.description })) : null, _jsx("div", { className: "mt-auto pt-2", children: data.state === 'running' ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "mt-1.5 flex items-center gap-1.5 text-xs", children: [_jsx(AgentIcon, { agentType: data.agentType, className: "h-3.5 w-3.5 shrink-0" }), _jsx("span", { className: "text-muted-foreground", children: getBadgeText(data) })] }), _jsx("div", { "data-testid": "feature-node-progress-bar", className: "bg-muted mt-1.5 h-1 w-full overflow-hidden rounded-full", children: _jsx("div", { className: "animate-indeterminate-progress bg-foreground/30 h-full w-1/3 rounded-full" }) })] })) : config.showProgressBar ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "text-muted-foreground flex items-center justify-end text-[10px]", children: _jsxs("span", { children: [data.progress, "%"] }) }), _jsx("div", { "data-testid": "feature-node-progress-bar", className: "bg-muted mt-1.5 h-1 w-full overflow-hidden rounded-full", children: _jsx("div", { className: cn('h-full rounded-full transition-all', config.progressClass), style: { width: `${data.progress}%` } }) })] })) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "text-muted-foreground text-[10px]", children: _jsx("span", { children: data.featureId }) }), _jsxs("div", { "data-testid": "feature-node-badge", className: cn('mt-1.5 flex items-center justify-center gap-1.5 rounded-full px-2.5 py-1 text-[11px] font-medium', config.badgeBgClass, config.badgeClass), children: [_jsx(Icon, { className: "h-3.5 w-3.5 shrink-0" }), _jsx("span", { className: "truncate", children: getBadgeText(data) })] })] })) })] }), data.onAction ? (_jsx(Handle, { type: "source", position: Position.Right, className: "h-0! w-0! border-0! bg-transparent!", children: _jsx("button", { type: "button", "aria-label": "Add", "data-testid": "feature-node-action-button", onClick: (e) => {
35
+ }, className: "nodrag text-muted-foreground hover:text-foreground -mt-1 -mr-1 p-1", children: _jsx(Settings, { className: "h-3.5 w-3.5" }) })) : null] }), _jsx("h3", { className: "mt-1 truncate text-sm font-bold", children: data.name }), data.description ? (_jsx("p", { "data-testid": "feature-node-description", className: "text-muted-foreground mt-0.5 truncate text-xs", children: data.description })) : null, _jsx("div", { className: "mt-auto pt-2", children: data.state === 'creating' ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "mt-1.5 flex items-center gap-1.5 text-xs", children: [_jsx(Icon, { className: "h-3.5 w-3.5 shrink-0 animate-spin" }), _jsx("span", { className: "text-muted-foreground", children: getBadgeText(data) })] }), _jsx("div", { "data-testid": "feature-node-progress-bar", className: "bg-muted mt-1.5 h-1 w-full overflow-hidden rounded-full", children: _jsx("div", { className: "motion-safe:animate-indeterminate-progress bg-foreground/30 h-full w-1/3 rounded-full" }) })] })) : data.state === 'running' ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "mt-1.5 flex items-center gap-1.5 text-xs", children: [_jsx(AgentIcon, { agentType: data.agentType, className: "h-3.5 w-3.5 shrink-0" }), _jsx("span", { className: "text-muted-foreground", children: getBadgeText(data) })] }), _jsx("div", { "data-testid": "feature-node-progress-bar", className: "bg-muted mt-1.5 h-1 w-full overflow-hidden rounded-full", children: _jsx("div", { className: "animate-indeterminate-progress bg-foreground/30 h-full w-1/3 rounded-full" }) })] })) : config.showProgressBar ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "text-muted-foreground flex items-center justify-end text-[10px]", children: _jsxs("span", { children: [data.progress, "%"] }) }), _jsx("div", { "data-testid": "feature-node-progress-bar", className: "bg-muted mt-1.5 h-1 w-full overflow-hidden rounded-full", children: _jsx("div", { className: cn('h-full rounded-full transition-all', config.progressClass), style: { width: `${data.progress}%` } }) })] })) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "text-muted-foreground text-[10px]", children: _jsx("span", { children: data.featureId }) }), _jsxs("div", { "data-testid": "feature-node-badge", className: cn('mt-1.5 flex items-center justify-center gap-1.5 rounded-full px-2.5 py-1 text-[11px] font-medium', config.badgeBgClass, config.badgeClass), children: [_jsx(Icon, { className: "h-3.5 w-3.5 shrink-0" }), _jsx("span", { className: "truncate", children: getBadgeText(data) })] })] })) })] }), data.onAction ? (_jsx(Handle, { type: "source", position: Position.Right, className: "h-0! w-0! border-0! bg-transparent!", children: _jsx("button", { type: "button", "aria-label": "Add", "data-testid": "feature-node-action-button", onClick: (e) => {
34
36
  e.stopPropagation();
35
37
  data.onAction?.();
36
38
  }, className: "nodrag absolute top-1/2 left-1/2 flex h-6 w-6 -translate-x-1/2 -translate-y-1/2 cursor-pointer items-center justify-center rounded-full bg-blue-500 text-white opacity-0 shadow-md transition-opacity group-hover:opacity-100 hover:bg-blue-600", children: _jsx(Plus, { className: "h-3.5 w-3.5" }) }) })) : data.showHandles ? (_jsx(Handle, { type: "source", position: Position.Right, isConnectable: false, className: "opacity-0!" })) : null] }));
@@ -11,6 +11,7 @@ export declare const WithAction: Story;
11
11
  export declare const LongContent: Story;
12
12
  export declare const DoneWithRuntime: Story;
13
13
  export declare const BlockedByFeature: Story;
14
+ export declare const Creating: Story;
14
15
  export declare const ErrorWithMessage: Story;
15
16
  export declare const Interactive: Story;
16
17
  //# sourceMappingURL=feature-node.stories.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"feature-node.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-node.stories.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAKvD,OAAO,KAAK,EACV,eAAe,EAIhB,MAAM,6BAA6B,CAAC;AAiCrC,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,eAAe,CAc/B,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,eAAe,CAAC,CAAC;AAEvC,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AA0DF,eAAO,MAAM,SAAS,EAAE,KAQvB,CAAC;AAWF,eAAO,MAAM,aAAa,EAAE,KAqB3B,CAAC;AAsEF,eAAO,MAAM,aAAa,EAAE,KAQ3B,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,KAMxB,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,KAOzB,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,KAW7B,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,KAW9B,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,KAW9B,CAAC;AA4EF,eAAO,MAAM,WAAW,EAAE,KAOzB,CAAC"}
1
+ {"version":3,"file":"feature-node.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-node.stories.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAKvD,OAAO,KAAK,EACV,eAAe,EAIhB,MAAM,6BAA6B,CAAC;AAiCrC,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,eAAe,CAc/B,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,eAAe,CAAC,CAAC;AAEvC,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAoEF,eAAO,MAAM,SAAS,EAAE,KAQvB,CAAC;AAWF,eAAO,MAAM,aAAa,EAAE,KAqB3B,CAAC;AAsEF,eAAO,MAAM,aAAa,EAAE,KAQ3B,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,KAMxB,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,KAOzB,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,KAW7B,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,KAW9B,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAUtB,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,KAW9B,CAAC;AA4EF,eAAO,MAAM,WAAW,EAAE,KAOzB,CAAC"}
@@ -29,6 +29,16 @@ export const Default = {
29
29
  render: (args) => _jsx(FeatureNodeCanvas, { data: args }),
30
30
  };
31
31
  const allStatesData = [
32
+ {
33
+ name: 'User Onboarding',
34
+ description: 'Implement guided onboarding wizard',
35
+ featureId: '',
36
+ lifecycle: 'requirements',
37
+ state: 'creating',
38
+ progress: 0,
39
+ repositoryPath: '/home/user/my-repo',
40
+ branch: '',
41
+ },
32
42
  {
33
43
  name: 'Auth Module',
34
44
  description: 'Implement OAuth2 authentication flow',
@@ -214,6 +224,17 @@ export const BlockedByFeature = {
214
224
  },
215
225
  render: (args) => _jsx(FeatureNodeCanvas, { data: args }),
216
226
  };
227
+ export const Creating = {
228
+ args: {
229
+ name: 'User Onboarding',
230
+ description: 'Implement guided onboarding wizard',
231
+ featureId: '',
232
+ lifecycle: 'requirements',
233
+ state: 'creating',
234
+ progress: 0,
235
+ },
236
+ render: (args) => _jsx(FeatureNodeCanvas, { data: args }),
237
+ };
217
238
  export const ErrorWithMessage = {
218
239
  args: {
219
240
  name: 'Email Service',
@@ -1,7 +1,7 @@
1
1
  export { AddRepositoryNode, type AddRepositoryNodeData, type AddRepositoryNodeType, } from './add-repository-node/index.js';
2
2
  export { ElapsedTime, formatElapsed } from './elapsed-time/index.js';
3
3
  export { EmptyState, type EmptyStateProps } from './empty-state/index.js';
4
- export { FeatureCreateDrawer, type FeatureCreateDrawerProps } from './feature-create-drawer/index.js';
4
+ export { FeatureCreateDrawer, type FeatureCreateDrawerProps, type FeatureCreatePayload, } from './feature-create-drawer/index.js';
5
5
  export { FeatureDrawer, type FeatureDrawerProps } from './feature-drawer/index.js';
6
6
  export { FeatureListItem, type FeatureListItemProps } from './feature-list-item/index.js';
7
7
  export { useFeatureActions, type FeatureActionsInput, type FeatureActionsState, } from './feature-drawer/use-feature-actions.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/components/common/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,GAC3B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,KAAK,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AAC7F,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,EACL,iBAAiB,EACjB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,GACzB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EACL,WAAW,EACX,sBAAsB,EACtB,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAC1B,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,sBAAsB,GAC5B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,mBAAmB,EAAE,KAAK,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AAC7F,OAAO,EAAE,kBAAkB,EAAE,KAAK,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAC1F,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EACL,cAAc,EACd,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,GACxB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,KAAK,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AACnG,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,KAAK,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAChG,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/components/common/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,GAC3B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,EACL,mBAAmB,EACnB,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,GAC1B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,EACL,iBAAiB,EACjB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,GACzB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EACL,WAAW,EACX,sBAAsB,EACtB,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAC1B,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,sBAAsB,GAC5B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,mBAAmB,EAAE,KAAK,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AAC7F,OAAO,EAAE,kBAAkB,EAAE,KAAK,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAC1F,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EACL,cAAc,EACd,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,GACxB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,KAAK,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AACnG,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,KAAK,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAChG,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC"}
@@ -1,7 +1,7 @@
1
1
  export { AddRepositoryNode, } from './add-repository-node/index.js';
2
2
  export { ElapsedTime, formatElapsed } from './elapsed-time/index.js';
3
3
  export { EmptyState } from './empty-state/index.js';
4
- export { FeatureCreateDrawer } from './feature-create-drawer/index.js';
4
+ export { FeatureCreateDrawer, } from './feature-create-drawer/index.js';
5
5
  export { FeatureDrawer } from './feature-drawer/index.js';
6
6
  export { FeatureListItem } from './feature-list-item/index.js';
7
7
  export { useFeatureActions, } from './feature-drawer/use-feature-actions.js';
@@ -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":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAE1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAK5E,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,2CA2DzF"}
1
+ {"version":3,"file":"control-center-inner.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/control-center-inner.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAE1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAK5E,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,2CAyDzF"}
@@ -6,12 +6,12 @@ import { FeatureDrawer, FeatureCreateDrawer } from '../../common/index.js';
6
6
  import { ControlCenterEmptyState } from './control-center-empty-state.js';
7
7
  import { useControlCenterState } from './use-control-center-state.js';
8
8
  export function ControlCenterInner({ initialNodes, initialEdges }) {
9
- const { nodes, edges, selectedNode, isCreateDrawerOpen, isSubmitting, isDeleting, pendingRepositoryPath, onNodesChange, handleConnect, handleAddFeature, handleAddFeatureToRepo, handleAddFeatureToFeature, handleAddRepository, handleNodeClick, clearSelection, handleCreateFeatureSubmit, handleDeleteFeature, closeCreateDrawer, } = useControlCenterState(initialNodes, initialEdges);
9
+ const { nodes, edges, selectedNode, isCreateDrawerOpen, isDeleting, pendingRepositoryPath, onNodesChange, handleConnect, handleAddFeature, handleAddFeatureToRepo, handleAddFeatureToFeature, handleAddRepository, handleNodeClick, clearSelection, handleCreateFeatureSubmit, handleDeleteFeature, closeCreateDrawer, } = useControlCenterState(initialNodes, initialEdges);
10
10
  // Listen for global "open create drawer" events from the sidebar
11
11
  useEffect(() => {
12
12
  const handler = () => handleAddFeature();
13
13
  window.addEventListener('shep:open-create-drawer', handler);
14
14
  return () => window.removeEventListener('shep:open-create-drawer', handler);
15
15
  }, [handleAddFeature]);
16
- return (_jsxs(_Fragment, { children: [_jsx(FeaturesCanvas, { nodes: nodes, edges: edges, onNodesChange: onNodesChange, onConnect: handleConnect, onAddFeature: handleAddFeature, onNodeAction: handleAddFeatureToFeature, onNodeClick: handleNodeClick, onPaneClick: clearSelection, onRepositoryAdd: handleAddFeatureToRepo, onRepositorySelect: handleAddRepository, emptyState: _jsx(ControlCenterEmptyState, { onRepositorySelect: handleAddRepository }) }), _jsx(FeatureDrawer, { selectedNode: selectedNode, onClose: clearSelection, onDelete: handleDeleteFeature, isDeleting: isDeleting }), _jsx(FeatureCreateDrawer, { open: isCreateDrawerOpen, onClose: closeCreateDrawer, onSubmit: handleCreateFeatureSubmit, repositoryPath: pendingRepositoryPath, isSubmitting: isSubmitting })] }));
16
+ return (_jsxs(_Fragment, { children: [_jsx(FeaturesCanvas, { nodes: nodes, edges: edges, onNodesChange: onNodesChange, onConnect: handleConnect, onAddFeature: handleAddFeature, onNodeAction: handleAddFeatureToFeature, onNodeClick: handleNodeClick, onPaneClick: clearSelection, onRepositoryAdd: handleAddFeatureToRepo, onRepositorySelect: handleAddRepository, emptyState: _jsx(ControlCenterEmptyState, { onRepositorySelect: handleAddRepository }) }), _jsx(FeatureDrawer, { selectedNode: selectedNode, onClose: clearSelection, onDelete: handleDeleteFeature, isDeleting: isDeleting }), _jsx(FeatureCreateDrawer, { open: isCreateDrawerOpen, onClose: closeCreateDrawer, onSubmit: handleCreateFeatureSubmit, repositoryPath: pendingRepositoryPath })] }));
17
17
  }
@@ -1,6 +1,6 @@
1
1
  import type { Connection, Edge, NodeChange } from '@xyflow/react';
2
2
  import type { FeatureNodeData } from '../../common/feature-node/index.js';
3
- import type { CreateFeatureInput } from '../../../../../../packages/core/src/application/use-cases/features/create/types.js';
3
+ import type { FeatureCreatePayload } from '../../common/feature-create-drawer/index.js';
4
4
  import type { CanvasNodeType } from '../../features/features-canvas/index.js';
5
5
  import { type LayoutDirection } from '../../../lib/layout-with-dagre.js';
6
6
  export interface ControlCenterState {
@@ -8,7 +8,6 @@ export interface ControlCenterState {
8
8
  edges: Edge[];
9
9
  selectedNode: FeatureNodeData | null;
10
10
  isCreateDrawerOpen: boolean;
11
- isSubmitting: boolean;
12
11
  pendingRepositoryPath: string;
13
12
  onNodesChange: (changes: NodeChange<CanvasNodeType>[]) => void;
14
13
  handleConnect: (connection: Connection) => void;
@@ -19,10 +18,11 @@ export interface ControlCenterState {
19
18
  handleAddFeatureToFeature: (featureNodeId: string) => void;
20
19
  handleAddRepository: (path: string) => void;
21
20
  handleLayout: (direction: LayoutDirection) => void;
22
- handleCreateFeatureSubmit: (data: CreateFeatureInput) => void;
21
+ handleCreateFeatureSubmit: (data: FeatureCreatePayload) => void;
23
22
  closeCreateDrawer: () => void;
24
23
  handleDeleteFeature: (featureId: string) => Promise<void>;
25
24
  isDeleting: boolean;
25
+ createFeatureNode: (sourceNodeId: string | null, dataOverride?: Partial<FeatureNodeData>) => string;
26
26
  }
27
27
  export declare function useControlCenterState(initialNodes: CanvasNodeType[], initialEdges: Edge[]): ControlCenterState;
28
28
  //# sourceMappingURL=use-control-center-state.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-control-center-state.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/use-control-center-state.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0DAA0D,CAAC;AACnG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAEhF,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,YAAY,EAAE,eAAe,GAAG,IAAI,CAAC;IACrC,kBAAkB,EAAE,OAAO,CAAC;IAC5B,YAAY,EAAE,OAAO,CAAC;IACtB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,aAAa,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,cAAc,CAAC,EAAE,KAAK,IAAI,CAAC;IAC/D,aAAa,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;IAChD,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,eAAe,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,UAAU,EAAE,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IACzE,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,sBAAsB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACrD,yBAAyB,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3D,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,YAAY,EAAE,CAAC,SAAS,EAAE,eAAe,KAAK,IAAI,CAAC;IACnD,yBAAyB,EAAE,CAAC,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAC9D,iBAAiB,EAAE,MAAM,IAAI,CAAC;IAC9B,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,UAAU,EAAE,OAAO,CAAC;CACrB;AAID,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,cAAc,EAAE,EAC9B,YAAY,EAAE,IAAI,EAAE,GACnB,kBAAkB,CA2UpB"}
1
+ {"version":3,"file":"use-control-center-state.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/use-control-center-state.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACtF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAEhF,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,YAAY,EAAE,eAAe,GAAG,IAAI,CAAC;IACrC,kBAAkB,EAAE,OAAO,CAAC;IAC5B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,aAAa,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,cAAc,CAAC,EAAE,KAAK,IAAI,CAAC;IAC/D,aAAa,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;IAChD,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,eAAe,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,UAAU,EAAE,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IACzE,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,sBAAsB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACrD,yBAAyB,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3D,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,YAAY,EAAE,CAAC,SAAS,EAAE,eAAe,KAAK,IAAI,CAAC;IACnD,yBAAyB,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAChE,iBAAiB,EAAE,MAAM,IAAI,CAAC;IAC9B,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,UAAU,EAAE,OAAO,CAAC;IACpB,iBAAiB,EAAE,CACjB,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,YAAY,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,KACpC,MAAM,CAAC;CACb;AAID,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,cAAc,EAAE,EAC9B,YAAY,EAAE,IAAI,EAAE,GACnB,kBAAkB,CA6YpB"}
@@ -11,9 +11,46 @@ export function useControlCenterState(initialNodes, initialEdges) {
11
11
  const [edges, setEdges] = useState(initialEdges);
12
12
  const [selectedNode, setSelectedNode] = useState(null);
13
13
  const [isCreateDrawerOpen, setIsCreateDrawerOpen] = useState(false);
14
- const [isSubmitting, setIsSubmitting] = useState(false);
15
14
  const [isDeleting, setIsDeleting] = useState(false);
16
15
  const [pendingRepoNodeId, setPendingRepoNodeId] = useState(null);
16
+ // Sync server props into local state when router.refresh() delivers new data
17
+ const initialNodeKey = initialNodes
18
+ .map((n) => n.id)
19
+ .sort()
20
+ .join(',');
21
+ const initialEdgeKey = initialEdges
22
+ .map((e) => e.id)
23
+ .sort()
24
+ .join(',');
25
+ useEffect(() => {
26
+ setNodes((currentNodes) => {
27
+ // Build a lookup of current node positions by ID
28
+ const currentById = new Map(currentNodes.map((n) => [n.id, n]));
29
+ // Identify optimistic "creating" nodes (they have temp IDs not in server data)
30
+ const serverIds = new Set(initialNodes.map((n) => n.id));
31
+ const creatingNodes = currentNodes.filter((n) => n.type === 'featureNode' &&
32
+ n.data.state === 'creating' &&
33
+ !serverIds.has(n.id));
34
+ // Merge server nodes with client positions
35
+ return initialNodes.map((serverNode) => {
36
+ // Node already exists on canvas — keep its position, update data
37
+ const existing = currentById.get(serverNode.id);
38
+ if (existing) {
39
+ return { ...serverNode, position: existing.position };
40
+ }
41
+ // New server node — inherit position from an optimistic creating node if available
42
+ if (serverNode.type === 'featureNode' && creatingNodes.length > 0) {
43
+ const donor = creatingNodes.shift();
44
+ return { ...serverNode, position: donor.position };
45
+ }
46
+ // Truly new node with no optimistic counterpart — use server position
47
+ return serverNode;
48
+ });
49
+ });
50
+ }, [initialNodeKey]); // eslint-disable-line react-hooks/exhaustive-deps
51
+ useEffect(() => {
52
+ setEdges(initialEdges);
53
+ }, [initialEdgeKey]); // eslint-disable-line react-hooks/exhaustive-deps
17
54
  const onNodesChange = useCallback((changes) => {
18
55
  setNodes((ns) => applyNodeChanges(changes, ns));
19
56
  }, []);
@@ -22,8 +59,11 @@ export function useControlCenterState(initialNodes, initialEdges) {
22
59
  }, []);
23
60
  const handleNodeClick = useCallback((_event, node) => {
24
61
  if (node.type === 'featureNode') {
62
+ const data = node.data;
63
+ if (data.state === 'creating')
64
+ return;
25
65
  setIsCreateDrawerOpen(false);
26
- setSelectedNode(node.data);
66
+ setSelectedNode(data);
27
67
  }
28
68
  }, []);
29
69
  // Keyboard shortcut: Escape to deselect
@@ -71,7 +111,7 @@ export function useControlCenterState(initialNodes, initialEdges) {
71
111
  description: dataOverride?.description ?? 'Describe what this feature does',
72
112
  featureId: `#${id.slice(-4)}`,
73
113
  lifecycle: 'requirements',
74
- state: 'running',
114
+ state: dataOverride?.state ?? 'running',
75
115
  progress: 0,
76
116
  repositoryPath: dataOverride?.repositoryPath ?? '',
77
117
  branch: dataOverride?.branch ?? '',
@@ -163,37 +203,50 @@ export function useControlCenterState(initialNodes, initialEdges) {
163
203
  },
164
204
  ]);
165
205
  }
166
- setSelectedNode(newFeatureData);
206
+ if (newFeatureData.state !== 'creating') {
207
+ setSelectedNode(newFeatureData);
208
+ }
209
+ return id;
167
210
  }, [edges]);
168
211
  const handleAddFeature = useCallback(() => {
169
212
  setSelectedNode(null);
170
213
  setIsCreateDrawerOpen(true);
171
214
  }, []);
172
- const handleCreateFeatureSubmit = useCallback(async (data) => {
173
- setIsSubmitting(true);
174
- try {
175
- const response = await fetch('/api/features/create', {
176
- method: 'POST',
177
- headers: { 'Content-Type': 'application/json' },
178
- body: JSON.stringify(data),
179
- });
215
+ const handleCreateFeatureSubmit = useCallback((data) => {
216
+ // 1. Insert optimistic node instantly
217
+ const tempId = createFeatureNode(pendingRepoNodeId, {
218
+ state: 'creating',
219
+ name: data.name,
220
+ description: data.description,
221
+ repositoryPath: data.repositoryPath,
222
+ });
223
+ // 2. Close drawer and clear pending state immediately
224
+ setIsCreateDrawerOpen(false);
225
+ setPendingRepoNodeId(null);
226
+ // 3. Fire API call in the background
227
+ fetch('/api/features/create', {
228
+ method: 'POST',
229
+ headers: { 'Content-Type': 'application/json' },
230
+ body: JSON.stringify(data),
231
+ })
232
+ .then(async (response) => {
180
233
  if (!response.ok) {
181
234
  const body = await response.json();
235
+ // Rollback: remove optimistic node and edge
236
+ setNodes((prev) => prev.filter((n) => n.id !== tempId));
237
+ setEdges((prev) => prev.filter((e) => e.target !== tempId));
182
238
  toast.error(body.error ?? 'Failed to create feature');
183
239
  return;
184
240
  }
185
- setIsCreateDrawerOpen(false);
186
- setPendingRepoNodeId(null);
187
- toast.success('Feature created successfully');
188
241
  router.refresh();
189
- }
190
- catch {
242
+ })
243
+ .catch(() => {
244
+ // Rollback: remove optimistic node and edge
245
+ setNodes((prev) => prev.filter((n) => n.id !== tempId));
246
+ setEdges((prev) => prev.filter((e) => e.target !== tempId));
191
247
  toast.error('Failed to create feature');
192
- }
193
- finally {
194
- setIsSubmitting(false);
195
- }
196
- }, [router]);
248
+ });
249
+ }, [router, createFeatureNode, pendingRepoNodeId]);
197
250
  const closeCreateDrawer = useCallback(() => {
198
251
  setIsCreateDrawerOpen(false);
199
252
  }, []);
@@ -273,7 +326,6 @@ export function useControlCenterState(initialNodes, initialEdges) {
273
326
  edges,
274
327
  selectedNode,
275
328
  isCreateDrawerOpen,
276
- isSubmitting,
277
329
  pendingRepositoryPath,
278
330
  onNodesChange,
279
331
  handleConnect,
@@ -288,5 +340,6 @@ export function useControlCenterState(initialNodes, initialEdges) {
288
340
  closeCreateDrawer,
289
341
  handleDeleteFeature,
290
342
  isDeleting,
343
+ createFeatureNode,
291
344
  };
292
345
  }