ai-design-system 0.1.43 → 0.1.45

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.
@@ -142,8 +142,8 @@ export const AIConversation = React.memo<AIConversationProps>(
142
142
  message={{
143
143
  id: subAgent.id,
144
144
  name: subAgent.subAgentName,
145
- description: typeof subAgent.input === 'string' ? subAgent.input : JSON.stringify(subAgent.input),
146
- content: typeof subAgent.output === 'string' ? subAgent.output : (subAgent.output ? JSON.stringify(subAgent.output) : ''),
145
+ description: undefined,
146
+ content: `**Task:** ${typeof subAgent.input === 'string' ? subAgent.input : JSON.stringify(subAgent.input)}${subAgent.output ? `\n\n**Output:** ${typeof subAgent.output === 'string' ? subAgent.output : JSON.stringify(subAgent.output)}` : ''}`,
147
147
  status: subAgent.status,
148
148
  toolCalls: [],
149
149
  }}
@@ -61,7 +61,7 @@ export const ControlledState: Story = {
61
61
  placeholder="Type something (controlled mode)"
62
62
  onSubmit={(message) => {
63
63
  setSubmittedMessages((prev) => [...prev, message.text || ""]);
64
- setValue(""); // Clear after submit
64
+ setValue("");
65
65
  }}
66
66
  />
67
67
 
@@ -164,7 +164,6 @@ export const CustomSubmitHandler: Story = {
164
164
  disabled={isSubmitting}
165
165
  onSubmit={async (message) => {
166
166
  setIsSubmitting(true);
167
- // Simulate async processing
168
167
  await new Promise((resolve) => setTimeout(resolve, 2000));
169
168
  setLastSubmitted(message.text || "");
170
169
  setIsSubmitting(false);
@@ -198,3 +197,79 @@ export const NoAttachmentUI: Story = {
198
197
  },
199
198
  },
200
199
  };
200
+
201
+ /**
202
+ * Dialog replacement — static content
203
+ * Shows PromptInput rendering a dialog instead of the input when `dialog` is set.
204
+ * The dialog content replaces the input entirely; no input is shown.
205
+ */
206
+ export const WithDialog: Story = {
207
+ args: {
208
+ placeholder: "This should not appear — dialog is active",
209
+ onSubmit: (message) => {
210
+ console.log("Submitted:", message);
211
+ },
212
+ dialog: (
213
+ <div className="rounded-lg border border-border bg-card p-6 text-center text-sm text-muted-foreground">
214
+ <p className="font-medium text-foreground">Dialog Content</p>
215
+ <p className="mt-1">This replaces the prompt input when the <code>dialog</code> prop is set.</p>
216
+ </div>
217
+ ),
218
+ },
219
+ parameters: {
220
+ docs: {
221
+ description: {
222
+ story:
223
+ "When the `dialog` prop is set, PromptInput renders that ReactNode instead of the input form. This is used by features like RefinementPanel to show file reviews or HITL approval cards in place of the input.",
224
+ },
225
+ },
226
+ },
227
+ };
228
+
229
+ /**
230
+ * Dialog replacement — interactive toggle
231
+ * Demonstrates switching between input mode and dialog mode.
232
+ */
233
+ export const ToggleDialog: Story = {
234
+ render: () => {
235
+ const [showDialog, setShowDialog] = useState(false);
236
+
237
+ return (
238
+ <div className="space-y-4">
239
+ <div className="rounded-md border border-border bg-muted p-4">
240
+ <label className="flex items-center gap-2">
241
+ <input
242
+ type="checkbox"
243
+ checked={showDialog}
244
+ onChange={(e) => setShowDialog(e.target.checked)}
245
+ />
246
+ <span className="text-sm">Show dialog (replaces input)</span>
247
+ </label>
248
+ </div>
249
+
250
+ <PromptInput
251
+ placeholder="Type a message..."
252
+ onSubmit={(message) => {
253
+ console.log("Submitted:", message);
254
+ }}
255
+ dialog={
256
+ showDialog ? (
257
+ <div className="rounded-lg border border-border bg-card p-6 text-center text-sm text-muted-foreground">
258
+ <p className="font-medium text-foreground">Dialog Active</p>
259
+ <p className="mt-1">Uncheck the toggle above to return to input mode.</p>
260
+ </div>
261
+ ) : undefined
262
+ }
263
+ />
264
+ </div>
265
+ );
266
+ },
267
+ parameters: {
268
+ docs: {
269
+ description: {
270
+ story:
271
+ "Interactive demonstration of the dialog replacement. Toggle the checkbox to switch between showing the prompt input and showing dialog content. This is the same pattern RefinementPanel uses for file review and HITL approval states.",
272
+ },
273
+ },
274
+ },
275
+ };
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import * as React from "react";
4
+ import type { ReactNode } from "react";
4
5
  import {
5
6
  PromptInput as AIPromptInput,
6
7
  PromptInputBody,
@@ -14,52 +15,22 @@ import {
14
15
  } from "@/components/ai-elements/prompt-input";
15
16
  import type { FormEvent } from "react";
16
17
 
17
- /**
18
- * PromptInput Block
19
- *
20
- * A thin wrapper around the PromptInput AI element that hides attachment functionality.
21
- * This block provides a simplified prompt input interface focused on text-only input.
22
- *
23
- * Features:
24
- * - Controlled and uncontrolled state support
25
- * - Disabled state (blocks submission, keeps textarea editable)
26
- * - Custom placeholder text
27
- * - No attachment UI (compositional hiding)
28
- */
29
-
30
18
  export interface PromptInputBlockProps
31
19
  extends Omit<
32
20
  AIPromptInputProps,
33
21
  "accept" | "multiple" | "maxFiles" | "maxFileSize" | "globalDrop" | "syncHiddenInput" | "onSubmit" | "onChange"
34
22
  > {
35
- /**
36
- * Blocks form submission when true. Textarea remains editable for drafts.
37
- */
38
23
  disabled?: boolean;
39
- /**
40
- * Placeholder text for the textarea input
41
- */
42
24
  placeholder?: string;
43
- /**
44
- * Controlled state value (when provided with onChange)
45
- */
46
25
  value?: string;
47
- /**
48
- * Controlled state change handler (when provided with value)
49
- */
50
26
  onChange?: (value: string) => void;
51
- /**
52
- * Submit handler called when form is submitted
53
- */
54
27
  onSubmit: (
55
28
  message: PromptInputMessage,
56
29
  event: FormEvent<HTMLFormElement>
57
30
  ) => void | Promise<void>;
31
+ dialog?: ReactNode;
58
32
  }
59
33
 
60
- /**
61
- * PromptInput component - text-only prompt input without attachments
62
- */
63
34
  export const PromptInput = React.memo<PromptInputBlockProps>(
64
35
  ({
65
36
  disabled = false,
@@ -67,9 +38,9 @@ export const PromptInput = React.memo<PromptInputBlockProps>(
67
38
  value,
68
39
  onChange,
69
40
  onSubmit,
41
+ dialog,
70
42
  ...props
71
43
  }) => {
72
- // Handle form submission with disabled state
73
44
  const handleSubmit = React.useCallback(
74
45
  (message: PromptInputMessage, event: FormEvent<HTMLFormElement>) => {
75
46
  if (disabled) {
@@ -81,10 +52,8 @@ export const PromptInput = React.memo<PromptInputBlockProps>(
81
52
  [disabled, onSubmit]
82
53
  );
83
54
 
84
- // Controlled mode: wrap in PromptInputProvider
85
55
  const isControlled = value !== undefined && onChange !== undefined;
86
56
 
87
- // Handle controlled state change
88
57
  const handleControlledChange = React.useCallback(
89
58
  (e: React.ChangeEvent<HTMLTextAreaElement>) => {
90
59
  if (onChange) {
@@ -94,26 +63,28 @@ export const PromptInput = React.memo<PromptInputBlockProps>(
94
63
  [onChange]
95
64
  );
96
65
 
97
- // Render the prompt input composition
66
+ if (dialog) {
67
+ return <>{dialog}</>;
68
+ }
69
+
98
70
  const promptInputContent = (
99
71
  <AIPromptInput onSubmit={handleSubmit} {...props}>
100
72
  <PromptInputBody>
101
73
  <PromptInputTextarea
102
74
  placeholder={placeholder}
103
- disabled={false} // Keep editable even when form is disabled
75
+ disabled={false}
104
76
  onChange={isControlled ? handleControlledChange : undefined}
105
77
  />
106
78
  </PromptInputBody>
107
79
  <PromptInputFooter>
108
80
  <PromptInputTools>
109
- {/* No attachment menu - compositionally hidden */}
81
+ {}
110
82
  </PromptInputTools>
111
83
  <PromptInputSubmit disabled={disabled} />
112
84
  </PromptInputFooter>
113
85
  </AIPromptInput>
114
86
  );
115
87
 
116
- // Wrap in provider for controlled mode
117
88
  if (isControlled) {
118
89
  return (
119
90
  <PromptInputProvider initialInput={value}>
@@ -0,0 +1,3 @@
1
+ export { PromptInput } from "./PromptInput";
2
+
3
+ export type { PromptInputBlockProps } from "./PromptInput";
@@ -10,6 +10,9 @@ export type { DocumentEditorWithCommentsProps } from './DocumentEditorWithCommen
10
10
  export { FileChangeQueue } from './FileChangeQueue'
11
11
  export type { FileChangeQueueProps } from './FileChangeQueue'
12
12
 
13
+ export { PromptInput } from './PromptInput'
14
+ export type { PromptInputBlockProps } from './PromptInput'
15
+
13
16
  // Blocks
14
17
  export * from './LayoutProvider'
15
18
  export * from './SectionLayout'
@@ -0,0 +1,115 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { ApprovalCard } from "./ApprovalCard";
3
+ import { fn } from "@storybook/test";
4
+
5
+ const meta: Meta<typeof ApprovalCard> = {
6
+ title: "Composites/ApprovalCard",
7
+ component: ApprovalCard,
8
+ parameters: {
9
+ layout: "centered",
10
+ },
11
+ args: {
12
+ onApprove: fn(),
13
+ onReject: fn(),
14
+ onEdit: fn(),
15
+ isProcessing: false,
16
+ },
17
+ } satisfies Meta<typeof ApprovalCard>;
18
+
19
+ export default meta;
20
+ type Story = StoryObj<typeof ApprovalCard>;
21
+
22
+ export const Default: Story = {
23
+ args: {
24
+ actionRequest: {
25
+ name: "send_email",
26
+ description: "Review email before sending to team",
27
+ args: {
28
+ to: "team@acme.com",
29
+ subject: "Q4 Results",
30
+ body: "Revenue grew 15% this quarter.",
31
+ },
32
+ },
33
+ reviewConfig: {
34
+ allowedDecisions: ["approve", "edit", "reject"],
35
+ },
36
+ },
37
+ };
38
+
39
+ export const SimpleApprovalOnly: Story = {
40
+ args: {
41
+ actionRequest: {
42
+ name: "publish_content",
43
+ description: "Publish video narration to production database",
44
+ args: {
45
+ document_id: "doc_123",
46
+ environment: "production",
47
+ },
48
+ },
49
+ reviewConfig: {
50
+ allowedDecisions: ["approve", "reject"],
51
+ },
52
+ },
53
+ };
54
+
55
+ export const MultilineArguments: Story = {
56
+ args: {
57
+ actionRequest: {
58
+ name: "apply_patch",
59
+ description: "Approve and apply code patch to target repository",
60
+ args: {
61
+ patch: `diff --git a/src/index.ts b/src/index.ts
62
+ index 83a2d78..d3345f1 100644
63
+ --- a/src/index.ts
64
+ +++ b/src/index.ts
65
+ @@ -10,3 +10,4 @@
66
+ export function hello() {
67
+ - console.log("hello");
68
+ + console.log("hello world!");
69
+ }`,
70
+ author: "AI Developer Agent",
71
+ },
72
+ },
73
+ reviewConfig: {
74
+ allowedDecisions: ["approve", "edit", "reject"],
75
+ },
76
+ },
77
+ };
78
+
79
+ export const InteractiveQuestion: Story = {
80
+ args: {
81
+ actionRequest: {
82
+ name: "ask_question",
83
+ args: {
84
+ question: "Where in the workflow timeline should the SEO review step run?",
85
+ options: [
86
+ "(Recommended) Before the main human content approval, so the editor can see the SEO recommendations and scores when reviewing the script.",
87
+ "After the main human content approval, so that the general content is finalized first before being tuned for SEO."
88
+ ],
89
+ is_multi_select: false
90
+ }
91
+ }
92
+ }
93
+ };
94
+
95
+ export const MultiQuestionPoll: Story = {
96
+ args: {
97
+ actionRequest: {
98
+ name: "ask_question",
99
+ args: {
100
+ questions: [
101
+ {
102
+ question: "Choose preferred publication channel:",
103
+ options: ["YouTube", "Vimeo", "TikTok", "Instagram Reels"],
104
+ is_multi_select: false
105
+ },
106
+ {
107
+ question: "Which departments need to sign off on budget?",
108
+ options: ["Marketing", "Finance", "Legal", "Operations"],
109
+ is_multi_select: true
110
+ }
111
+ ]
112
+ }
113
+ }
114
+ }
115
+ };