ai-design-system 0.1.45 → 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/blocks/SectionLayout/SectionLayout.tsx +2 -2
- package/components/composites/OrchestratorMessage/OrchestratorMessage.tsx +8 -2
- package/components/composites/SpecialistMessage/SpecialistMessage.tsx +6 -2
- package/components/features/AIDocEditor/AIDocEditor.tsx +38 -12
- package/components/features/RefinementPanel/RefinementPanel.mocks.ts +58 -2
- package/components/features/RefinementPanel/RefinementPanel.tsx +57 -20
- package/dist/index.cjs +88 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +45 -3
- package/dist/index.d.ts +2 -0
- package/dist/index.js +88 -29
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -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
|
>
|
|
@@ -143,7 +152,7 @@ export const AIConversation = React.memo<AIConversationProps>(
|
|
|
143
152
|
id: subAgent.id,
|
|
144
153
|
name: subAgent.subAgentName,
|
|
145
154
|
description: undefined,
|
|
146
|
-
content:
|
|
155
|
+
content: typeof subAgent.output === 'string' ? subAgent.output : (subAgent.output ? JSON.stringify(subAgent.output) : ''),
|
|
147
156
|
status: subAgent.status,
|
|
148
157
|
toolCalls: [],
|
|
149
158
|
}}
|
|
@@ -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>
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
Message,
|
|
4
4
|
MessageContent,
|
|
5
5
|
MessageAvatar,
|
|
6
|
+
MessageTypingIndicator,
|
|
6
7
|
} from "@/components/ai-elements/message"
|
|
7
8
|
|
|
8
9
|
/**
|
|
@@ -17,6 +18,7 @@ export interface OrchestratorMessageData {
|
|
|
17
18
|
content: string
|
|
18
19
|
avatarSrc?: string
|
|
19
20
|
avatarName?: string
|
|
21
|
+
isLoading?: boolean
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
export interface OrchestratorMessageProps {
|
|
@@ -53,9 +55,13 @@ export const OrchestratorMessage = React.memo<OrchestratorMessageProps>(
|
|
|
53
55
|
/>
|
|
54
56
|
)}
|
|
55
57
|
|
|
56
|
-
<div className="flex-1">
|
|
58
|
+
<div className="flex-1 min-w-0">
|
|
57
59
|
{/* Orchestrator's message content */}
|
|
58
|
-
{
|
|
60
|
+
{message.isLoading ? (
|
|
61
|
+
<MessageContent variant="contained" className="w-fit pr-6">
|
|
62
|
+
<MessageTypingIndicator />
|
|
63
|
+
</MessageContent>
|
|
64
|
+
) : hasContent && (
|
|
59
65
|
<MessageContent variant="contained">
|
|
60
66
|
{message.content}
|
|
61
67
|
</MessageContent>
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
} from "@/components/ai-elements/plan"
|
|
10
10
|
import { ToolCallDisplay, type ToolCall } from "@/components/composites/ToolCallDisplay"
|
|
11
11
|
import { cn } from "@/lib/utils"
|
|
12
|
+
import { Streamdown } from "streamdown"
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* SpecialistMessage Block
|
|
@@ -85,9 +86,12 @@ export const SpecialistMessage = React.memo<SpecialistMessageProps>(
|
|
|
85
86
|
<PlanContent>
|
|
86
87
|
{/* Message content */}
|
|
87
88
|
{hasContent && (
|
|
88
|
-
<
|
|
89
|
+
<Streamdown
|
|
90
|
+
className="size-full [&>*:first-child]:mt-0 [&>*:last-child]:mb-0"
|
|
91
|
+
mode="static"
|
|
92
|
+
>
|
|
89
93
|
{message.content}
|
|
90
|
-
</
|
|
94
|
+
</Streamdown>
|
|
91
95
|
)}
|
|
92
96
|
|
|
93
97
|
{/* Tool calls */}
|
|
@@ -38,6 +38,7 @@ import { DocumentTabBar } from '@/components/composites/DocumentTabBar'
|
|
|
38
38
|
import { cn } from '@/lib/utils'
|
|
39
39
|
import type { JSONContent } from '@tiptap/core'
|
|
40
40
|
import type { Annotation, User } from '@/types/ai-editor/annotations'
|
|
41
|
+
import { Streamdown } from 'streamdown'
|
|
41
42
|
|
|
42
43
|
interface DocumentWithAnnotations {
|
|
43
44
|
file: { id: string; name: string; isDirty?: boolean; format?: 'json' | 'markdown' | string; lastModified?: number }
|
|
@@ -145,6 +146,7 @@ function isMultiTabMode(props: AIDocEditorProps): props is AIDocEditorMultiTabPr
|
|
|
145
146
|
* - Support for comments, suggestions, and block additions
|
|
146
147
|
* - Controlled component pattern (all state managed by parent)
|
|
147
148
|
* - Backward-compatible with single-document consumers
|
|
149
|
+
* - Markdown content rendered via Streamdown for proper formatting
|
|
148
150
|
*
|
|
149
151
|
* Note: The refinement panel (right sidebar with Accept All/Reject All) is a
|
|
150
152
|
* separate component in the parent application, not part of this feature.
|
|
@@ -156,7 +158,7 @@ export const AIDocEditor = React.memo<AIDocEditorProps>(
|
|
|
156
158
|
mode,
|
|
157
159
|
onAnnotationAdd,
|
|
158
160
|
onAnnotationUpdate,
|
|
159
|
-
onAnnotationDelete: _onAnnotationDelete,
|
|
161
|
+
onAnnotationDelete: _onAnnotationDelete,
|
|
160
162
|
className,
|
|
161
163
|
} = props
|
|
162
164
|
|
|
@@ -199,6 +201,16 @@ export const AIDocEditor = React.memo<AIDocEditorProps>(
|
|
|
199
201
|
* Single-document mode
|
|
200
202
|
*/
|
|
201
203
|
if (!isMultiTab) {
|
|
204
|
+
const isMarkdown = props.format === 'markdown'
|
|
205
|
+
if (isMarkdown) {
|
|
206
|
+
return (
|
|
207
|
+
<div className={cn('ai-doc-editor p-6', className)}>
|
|
208
|
+
<Streamdown mode="streaming">
|
|
209
|
+
{props.content as string}
|
|
210
|
+
</Streamdown>
|
|
211
|
+
</div>
|
|
212
|
+
)
|
|
213
|
+
}
|
|
202
214
|
return (
|
|
203
215
|
<DocumentEditorWithComments
|
|
204
216
|
content={props.content}
|
|
@@ -234,6 +246,8 @@ export const AIDocEditor = React.memo<AIDocEditorProps>(
|
|
|
234
246
|
)
|
|
235
247
|
}
|
|
236
248
|
|
|
249
|
+
const isMarkdown = currentDocument.file.format === 'markdown'
|
|
250
|
+
|
|
237
251
|
return (
|
|
238
252
|
<div className={cn('ai-doc-editor flex flex-col h-full', className)}>
|
|
239
253
|
<DocumentTabBar
|
|
@@ -243,17 +257,29 @@ export const AIDocEditor = React.memo<AIDocEditorProps>(
|
|
|
243
257
|
onTabClose={props.onTabClose}
|
|
244
258
|
/>
|
|
245
259
|
<div className="flex-1 overflow-auto">
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
260
|
+
{isMarkdown ? (
|
|
261
|
+
<div className="p-6">
|
|
262
|
+
<Streamdown
|
|
263
|
+
mode="streaming"
|
|
264
|
+
isAnimating
|
|
265
|
+
className="[&>*:first-child]:mt-0 [&>*:last-child]:mb-0"
|
|
266
|
+
>
|
|
267
|
+
{currentDocument.content as string}
|
|
268
|
+
</Streamdown>
|
|
269
|
+
</div>
|
|
270
|
+
) : (
|
|
271
|
+
<DocumentEditorWithComments
|
|
272
|
+
content={currentDocument.content}
|
|
273
|
+
format={currentDocument.file.format as 'json' | 'markdown' | undefined}
|
|
274
|
+
annotations={currentDocument.annotations}
|
|
275
|
+
currentUserId={currentUser.id}
|
|
276
|
+
currentUserName={currentUser.name}
|
|
277
|
+
readOnly={mode === 'readonly'}
|
|
278
|
+
onAnnotationAdd={handleAnnotationAdd}
|
|
279
|
+
onAnnotationUpdate={handleAnnotationUpdate}
|
|
280
|
+
className="p-6"
|
|
281
|
+
/>
|
|
282
|
+
)}
|
|
257
283
|
</div>
|
|
258
284
|
</div>
|
|
259
285
|
)
|
|
@@ -105,6 +105,62 @@ export const reviewStateMessages: RefinementMessage[] = [
|
|
|
105
105
|
},
|
|
106
106
|
],
|
|
107
107
|
},
|
|
108
|
+
{
|
|
109
|
+
id: '3',
|
|
110
|
+
type: 'human',
|
|
111
|
+
role: 'user',
|
|
112
|
+
content: 'Can we also make sure the colors meet the contrast requirements for dark mode?',
|
|
113
|
+
avatarSrc:
|
|
114
|
+
'https://images.unsplash.com/photo-1494790108755-2616b612b786?w=32&h=32&fit=crop&crop=face',
|
|
115
|
+
avatarName: 'User',
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
id: '4',
|
|
119
|
+
type: 'ai',
|
|
120
|
+
role: 'orchestrator',
|
|
121
|
+
content: 'Absolutely. I will have the accessibility specialist double-check the color contrast ratios in both light and dark mode.',
|
|
122
|
+
avatarSrc:
|
|
123
|
+
'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=32&h=32&fit=crop&crop=face',
|
|
124
|
+
avatarName: 'Coordinator',
|
|
125
|
+
subAgents: [
|
|
126
|
+
{
|
|
127
|
+
id: 'agent_3',
|
|
128
|
+
name: 'accessibility-specialist',
|
|
129
|
+
subAgentName: 'a11y-agent',
|
|
130
|
+
input: 'Verify color contrast ratios for the new Button styles in both light and dark mode themes according to WCAG 2.1 AA.',
|
|
131
|
+
output: 'Contrast ratios checked: Primary button passes at 4.5:1 on light mode, but fails on dark mode (3.2:1). Adjusting the primary-dark token to #66b2ff to meet the 4.5:1 requirement.',
|
|
132
|
+
status: 'completed',
|
|
133
|
+
}
|
|
134
|
+
]
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
id: '5',
|
|
138
|
+
type: 'human',
|
|
139
|
+
role: 'user',
|
|
140
|
+
content: 'Perfect! How about the hover state? Does it also pass?',
|
|
141
|
+
avatarSrc:
|
|
142
|
+
'https://images.unsplash.com/photo-1494790108755-2616b612b786?w=32&h=32&fit=crop&crop=face',
|
|
143
|
+
avatarName: 'User',
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
id: '6',
|
|
147
|
+
type: 'ai',
|
|
148
|
+
role: 'orchestrator',
|
|
149
|
+
content: 'Good catch. Let me check the hover and active states for both themes.',
|
|
150
|
+
avatarSrc:
|
|
151
|
+
'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=32&h=32&fit=crop&crop=face',
|
|
152
|
+
avatarName: 'Coordinator',
|
|
153
|
+
subAgents: [
|
|
154
|
+
{
|
|
155
|
+
id: 'agent_4',
|
|
156
|
+
name: 'accessibility-specialist',
|
|
157
|
+
subAgentName: 'a11y-agent',
|
|
158
|
+
input: 'Verify color contrast for hover and active states.',
|
|
159
|
+
output: 'Hover states pass contrast checks. The active state requires a slight tweak for the outline variant. Applied fixes.',
|
|
160
|
+
status: 'completed',
|
|
161
|
+
}
|
|
162
|
+
]
|
|
163
|
+
},
|
|
108
164
|
]
|
|
109
165
|
|
|
110
166
|
/**
|
|
@@ -114,7 +170,7 @@ export const reviewStateMessages: RefinementMessage[] = [
|
|
|
114
170
|
* Sample single-question approval request (HITL interactive question)
|
|
115
171
|
*/
|
|
116
172
|
export const approvalQuestionRequest: ActionRequest = {
|
|
117
|
-
name: "
|
|
173
|
+
name: "ask_user",
|
|
118
174
|
description: "The agent needs your input to determine the workflow order",
|
|
119
175
|
args: {
|
|
120
176
|
question: "Where in the workflow timeline should the SEO review step run?",
|
|
@@ -130,7 +186,7 @@ export const approvalQuestionRequest: ActionRequest = {
|
|
|
130
186
|
* Sample multi-question approval request (HITL poll with multiple questions)
|
|
131
187
|
*/
|
|
132
188
|
export const approvalMultiQuestionRequest: ActionRequest = {
|
|
133
|
-
name: "
|
|
189
|
+
name: "ask_user",
|
|
134
190
|
description: "The agent needs your preferences to proceed",
|
|
135
191
|
args: {
|
|
136
192
|
questions: [
|
|
@@ -35,6 +35,7 @@ export interface RefinementMessage {
|
|
|
35
35
|
avatarName?: string;
|
|
36
36
|
toolCalls?: ToolCall[];
|
|
37
37
|
subAgents?: SubAgent[];
|
|
38
|
+
isLoading?: boolean;
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
/**
|
|
@@ -157,12 +158,47 @@ export const RefinementPanel = React.memo<RefinementPanelProps>(
|
|
|
157
158
|
onApprovalReject?.(reason);
|
|
158
159
|
}, [onApprovalReject]);
|
|
159
160
|
|
|
161
|
+
// Extract pending ask_user tool call from messages
|
|
162
|
+
const pendingAskUser = React.useMemo(() => {
|
|
163
|
+
// Look from the end
|
|
164
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
165
|
+
const msg = messages[i];
|
|
166
|
+
if (msg.toolCalls) {
|
|
167
|
+
const askUserTc = msg.toolCalls.find(tc => tc.name === "ask_user" && tc.status === "pending");
|
|
168
|
+
if (askUserTc) {
|
|
169
|
+
return askUserTc;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return null;
|
|
174
|
+
}, [messages]);
|
|
175
|
+
|
|
176
|
+
const activeApprovalRequest = React.useMemo(() => {
|
|
177
|
+
if (approvalRequest) return approvalRequest;
|
|
178
|
+
if (pendingAskUser) {
|
|
179
|
+
return {
|
|
180
|
+
name: pendingAskUser.name,
|
|
181
|
+
args: pendingAskUser.args,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
return undefined;
|
|
185
|
+
}, [approvalRequest, pendingAskUser]);
|
|
186
|
+
|
|
160
187
|
// Handle edit for HITL approval request
|
|
161
188
|
const handleApprovalEdit = React.useCallback((editedArgs: Record<string, unknown>) => {
|
|
162
189
|
setApprovalCardState("approval-responded");
|
|
163
190
|
setApprovalCardApproval({ approved: true });
|
|
164
191
|
onApprovalEdit?.(editedArgs);
|
|
165
|
-
|
|
192
|
+
|
|
193
|
+
// If there's a pendingAskUser and answers are provided, submit them!
|
|
194
|
+
if (pendingAskUser && editedArgs.answers) {
|
|
195
|
+
const answers = Array.isArray(editedArgs.answers) ? editedArgs.answers : [editedArgs.answers];
|
|
196
|
+
const text = answers.join(', ');
|
|
197
|
+
// Submit the text response
|
|
198
|
+
const dummyEvent = { preventDefault: () => {} } as React.FormEvent<HTMLFormElement>;
|
|
199
|
+
onSubmit({ text, files: [] }, dummyEvent);
|
|
200
|
+
}
|
|
201
|
+
}, [onApprovalEdit, pendingAskUser, onSubmit]);
|
|
166
202
|
|
|
167
203
|
// Reset file change state when fileChanges are cleared
|
|
168
204
|
React.useEffect(() => {
|
|
@@ -172,24 +208,22 @@ export const RefinementPanel = React.memo<RefinementPanelProps>(
|
|
|
172
208
|
}
|
|
173
209
|
}, [fileChanges.length]);
|
|
174
210
|
|
|
175
|
-
// Reset approval card state when
|
|
211
|
+
// Reset approval card state when activeApprovalRequest is cleared
|
|
176
212
|
React.useEffect(() => {
|
|
177
|
-
if (!
|
|
213
|
+
if (!activeApprovalRequest) {
|
|
178
214
|
setApprovalCardState("approval-requested");
|
|
179
215
|
setApprovalCardApproval({});
|
|
180
216
|
}
|
|
181
|
-
}, [
|
|
182
|
-
|
|
183
|
-
|
|
217
|
+
}, [activeApprovalRequest]);
|
|
184
218
|
|
|
185
219
|
// Construct dialog content that replaces the prompt input when active
|
|
186
220
|
let dialog: React.ReactNode = null;
|
|
187
221
|
|
|
188
|
-
if (
|
|
222
|
+
if (activeApprovalRequest) {
|
|
189
223
|
dialog = (
|
|
190
224
|
<div className="w-full flex-shrink-0 bg-background p-4">
|
|
191
225
|
<ApprovalCard
|
|
192
|
-
actionRequest={
|
|
226
|
+
actionRequest={activeApprovalRequest}
|
|
193
227
|
reviewConfig={reviewConfig}
|
|
194
228
|
onApprove={handleApprovalApprove}
|
|
195
229
|
onReject={handleApprovalReject}
|
|
@@ -218,18 +252,21 @@ export const RefinementPanel = React.memo<RefinementPanelProps>(
|
|
|
218
252
|
}
|
|
219
253
|
|
|
220
254
|
return (
|
|
221
|
-
<div className={`relative flex h-
|
|
222
|
-
<
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
255
|
+
<div className={`relative flex h-full flex-col ${className || ""}`}>
|
|
256
|
+
<div className="flex-1 flex flex-col pb-4 min-h-0">
|
|
257
|
+
<AIConversation
|
|
258
|
+
messages={messages}
|
|
259
|
+
showAvatars={true}
|
|
260
|
+
className="relative"
|
|
261
|
+
/>
|
|
262
|
+
</div>
|
|
263
|
+
<div className="sticky bottom-0 z-10 bg-background border-t">
|
|
264
|
+
<PromptInput
|
|
265
|
+
dialog={dialog}
|
|
266
|
+
placeholder={placeholder}
|
|
267
|
+
onSubmit={onSubmit}
|
|
268
|
+
/>
|
|
269
|
+
</div>
|
|
233
270
|
</div>
|
|
234
271
|
);
|
|
235
272
|
}
|