ai-design-system 0.1.44 → 0.1.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/ai-elements/code-block.tsx +2 -2
- package/components/ai-elements/message.tsx +10 -2
- package/components/blocks/AIConversation/AIConversation.tsx +11 -2
- package/components/{composites → blocks}/PromptInput/PromptInput.stories.tsx +77 -2
- package/components/{composites → blocks}/PromptInput/PromptInput.tsx +9 -38
- package/components/blocks/PromptInput/index.ts +3 -0
- package/components/blocks/SectionLayout/SectionLayout.tsx +2 -2
- package/components/blocks/index.ts +3 -0
- package/components/composites/ApprovalCard/ApprovalCard.stories.tsx +115 -0
- package/components/composites/ApprovalCard/ApprovalCard.tsx +533 -0
- package/components/composites/ApprovalCard/index.ts +2 -0
- package/components/composites/Confirmation/Confirmation.stories.tsx +1 -1
- package/components/composites/OrchestratorMessage/OrchestratorMessage.tsx +8 -2
- package/components/composites/SpecialistMessage/SpecialistMessage.tsx +6 -2
- package/components/composites/index.ts +4 -3
- package/components/features/AIDocEditor/AIDocEditor.tsx +38 -12
- package/components/features/RefinementPanel/RefinementPanel.mocks.ts +95 -0
- package/components/features/RefinementPanel/RefinementPanel.stories.tsx +147 -1
- package/components/features/RefinementPanel/RefinementPanel.tsx +151 -36
- package/components/features/RefinementPanel/index.ts +3 -0
- package/components/features/RefinementPanel/useRefinementPanel.d.ts +19 -0
- package/components/features/RefinementPanel/useRefinementPanel.mock.ts +77 -35
- package/components/index.ts +2 -1
- package/dist/index.cjs +611 -66
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +133 -3
- package/dist/index.d.ts +84 -34
- package/dist/index.js +612 -68
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/components/composites/PromptInput/index.ts +0 -8
|
@@ -110,12 +110,12 @@ export const CodeBlock = ({
|
|
|
110
110
|
>
|
|
111
111
|
<div className="relative">
|
|
112
112
|
<div
|
|
113
|
-
className="overflow-hidden dark:hidden [&>pre]:m-0 [&>pre]:bg-background! [&>pre]:p-4 [&>pre]:text-foreground! [&>pre]:text-sm [&_code]:font-mono [&_code]:text-sm"
|
|
113
|
+
className="overflow-x-auto overflow-y-hidden dark:hidden [&>pre]:m-0 [&>pre]:bg-background! [&>pre]:p-4 [&>pre]:text-foreground! [&>pre]:text-sm [&_code]:font-mono [&_code]:text-sm"
|
|
114
114
|
// biome-ignore lint/security/noDangerouslySetInnerHtml: "this is needed."
|
|
115
115
|
dangerouslySetInnerHTML={{ __html: html }}
|
|
116
116
|
/>
|
|
117
117
|
<div
|
|
118
|
-
className="hidden overflow-hidden dark:block [&>pre]:m-0 [&>pre]:bg-background! [&>pre]:p-4 [&>pre]:text-foreground! [&>pre]:text-sm [&_code]:font-mono [&_code]:text-sm"
|
|
118
|
+
className="hidden overflow-x-auto overflow-y-hidden dark:block [&>pre]:m-0 [&>pre]:bg-background! [&>pre]:p-4 [&>pre]:text-foreground! [&>pre]:text-sm [&_code]:font-mono [&_code]:text-sm"
|
|
119
119
|
// biome-ignore lint/security/noDangerouslySetInnerHtml: "this is needed."
|
|
120
120
|
dangerouslySetInnerHTML={{ __html: darkHtml }}
|
|
121
121
|
/>
|
|
@@ -16,7 +16,7 @@ export const Message = ({ className, from, ...props }: MessageProps) => (
|
|
|
16
16
|
<div
|
|
17
17
|
className={cn(
|
|
18
18
|
"group flex w-full items-start gap-2 py-2",
|
|
19
|
-
from === "user" ? "is-user flex-row-reverse" : "is-assistant
|
|
19
|
+
from === "user" ? "is-user flex-row-reverse" : "is-assistant",
|
|
20
20
|
className
|
|
21
21
|
)}
|
|
22
22
|
{...props}
|
|
@@ -24,7 +24,7 @@ export const Message = ({ className, from, ...props }: MessageProps) => (
|
|
|
24
24
|
);
|
|
25
25
|
|
|
26
26
|
const messageContentVariants = cva(
|
|
27
|
-
"is-user:dark flex flex-col gap-2 overflow-hidden rounded-lg text-sm group-[.is-user]:ml-auto",
|
|
27
|
+
"is-user:dark flex flex-col gap-2 overflow-hidden rounded-lg text-sm group-[.is-user]:ml-auto min-w-0",
|
|
28
28
|
{
|
|
29
29
|
variants: {
|
|
30
30
|
variant: {
|
|
@@ -78,3 +78,11 @@ export const MessageAvatar = ({
|
|
|
78
78
|
<AvatarFallback>{name?.slice(0, 2) || "ME"}</AvatarFallback>
|
|
79
79
|
</Avatar>
|
|
80
80
|
);
|
|
81
|
+
|
|
82
|
+
export const MessageTypingIndicator = ({ className, ...props }: HTMLAttributes<HTMLDivElement>) => (
|
|
83
|
+
<div className={cn("flex items-center space-x-1 py-1.5 px-1 h-[24px]", className)} {...props}>
|
|
84
|
+
<div className="w-1.5 h-1.5 bg-current rounded-full animate-bounce [animation-delay:-0.3s]"></div>
|
|
85
|
+
<div className="w-1.5 h-1.5 bg-current rounded-full animate-bounce [animation-delay:-0.15s]"></div>
|
|
86
|
+
<div className="w-1.5 h-1.5 bg-current rounded-full animate-bounce"></div>
|
|
87
|
+
</div>
|
|
88
|
+
);
|
|
@@ -11,6 +11,7 @@ import { UserMessage } from "@/components/composites/UserMessage"
|
|
|
11
11
|
import { SpecialistMessage } from "@/components/composites/SpecialistMessage"
|
|
12
12
|
import { OrchestratorMessage } from "@/components/composites/OrchestratorMessage"
|
|
13
13
|
import { ToolCallDisplay } from "@/components/composites/ToolCallDisplay"
|
|
14
|
+
import { ApprovalCard } from "@/components/composites/ApprovalCard"
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* AIConversation Section
|
|
@@ -31,6 +32,7 @@ interface AIMessage {
|
|
|
31
32
|
avatarName?: string;
|
|
32
33
|
toolCalls?: ToolCall[];
|
|
33
34
|
subAgents?: SubAgent[];
|
|
35
|
+
isLoading?: boolean;
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
export interface AIConversationProps
|
|
@@ -114,11 +116,17 @@ export const AIConversation = React.memo<AIConversationProps>(
|
|
|
114
116
|
const subAgents = message.subAgents || []
|
|
115
117
|
|
|
116
118
|
// Filter tool calls that aren't "task" type (those become sub-agents)
|
|
119
|
+
// Also completely hide "ask_user" and "ask_question" tools so they are ONLY rendered in the prompt input area
|
|
117
120
|
const directToolCalls =
|
|
118
121
|
message.toolCalls?.filter(
|
|
119
|
-
(tc) => tc.name !== "task"
|
|
122
|
+
(tc) => tc.name !== "task" && tc.name !== "ask_user"
|
|
120
123
|
) || []
|
|
121
124
|
|
|
125
|
+
const hasContent = message.content && message.content.trim() !== ""
|
|
126
|
+
if (!hasContent && directToolCalls.length === 0 && subAgents.length === 0 && !message.isLoading) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
|
|
122
130
|
return (
|
|
123
131
|
<OrchestratorMessage
|
|
124
132
|
key={message.id}
|
|
@@ -127,6 +135,7 @@ export const AIConversation = React.memo<AIConversationProps>(
|
|
|
127
135
|
content: message.content,
|
|
128
136
|
avatarSrc: message.avatarSrc,
|
|
129
137
|
avatarName: message.avatarName,
|
|
138
|
+
isLoading: message.isLoading,
|
|
130
139
|
}}
|
|
131
140
|
showAvatar={showAvatars}
|
|
132
141
|
>
|
|
@@ -142,7 +151,7 @@ export const AIConversation = React.memo<AIConversationProps>(
|
|
|
142
151
|
message={{
|
|
143
152
|
id: subAgent.id,
|
|
144
153
|
name: subAgent.subAgentName,
|
|
145
|
-
description:
|
|
154
|
+
description: undefined,
|
|
146
155
|
content: typeof subAgent.output === 'string' ? subAgent.output : (subAgent.output ? JSON.stringify(subAgent.output) : ''),
|
|
147
156
|
status: subAgent.status,
|
|
148
157
|
toolCalls: [],
|
|
@@ -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("");
|
|
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
|
-
|
|
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}
|
|
75
|
+
disabled={false}
|
|
104
76
|
onChange={isControlled ? handleControlledChange : undefined}
|
|
105
77
|
/>
|
|
106
78
|
</PromptInputBody>
|
|
107
79
|
<PromptInputFooter>
|
|
108
80
|
<PromptInputTools>
|
|
109
|
-
{
|
|
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}>
|
|
@@ -28,11 +28,11 @@ export const SectionLayout = React.memo<SectionLayoutProps>(
|
|
|
28
28
|
...section,
|
|
29
29
|
resizable,
|
|
30
30
|
content: (
|
|
31
|
-
<div className="h-full min-h-0 flex flex-col overflow-
|
|
31
|
+
<div className="h-full min-h-0 flex flex-col overflow-auto">
|
|
32
32
|
{section.header && (
|
|
33
33
|
<AppHeader {...section.header} />
|
|
34
34
|
)}
|
|
35
|
-
<div className="min-h-0 flex-1 overflow-
|
|
35
|
+
<div className="min-h-0 flex-1 overflow-hidden">
|
|
36
36
|
{section.content}
|
|
37
37
|
</div>
|
|
38
38
|
</div>
|
|
@@ -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
|
+
};
|