ai-design-system 0.1.57 → 0.1.59
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/reasoning.tsx +1 -2
- package/components/blocks/AIConversation/AIConversation.tsx +113 -28
- package/components/blocks/FormReportsSection/FormReportsSection.tsx +3 -0
- package/components/composites/AppBreadcrumb/AppBreadcrumb.stories.tsx +46 -0
- package/components/composites/AppBreadcrumb/AppBreadcrumb.tsx +77 -0
- package/components/composites/AppBreadcrumb/index.ts +2 -0
- package/components/composites/DataTable/DraggableRow.tsx +5 -2
- package/components/composites/DataTable/EnhancedDataTable.tsx +7 -1
- package/components/composites/DataTable/table-types.ts +1 -0
- package/components/composites/FormReports/FormReportsTable.tsx +10 -3
- package/components/composites/OrchestratorMessage/OrchestratorMessage.tsx +1 -1
- package/components/composites/ProjectSwitcher/ProjectSwitcher.tsx +23 -10
- package/components/composites/ReasoningDisplay/ReasoningDisplay.tsx +8 -3
- package/components/composites/SessionHeader/SessionHeader.stories.tsx +50 -0
- package/components/composites/SessionHeader/SessionHeader.tsx +86 -0
- package/components/composites/SessionHeader/index.ts +2 -0
- package/components/composites/SpecialistMessage/SpecialistMessage.tsx +1 -1
- package/components/composites/StreamingMarkdown.tsx +120 -0
- package/components/composites/TriggerNode/TriggerNode.tsx +1 -1
- package/components/composites/index.ts +11 -0
- package/components/{ai-elements → composites}/response.tsx +3 -3
- package/components/features/FormReportsFeature/FormReportsFeature.tsx +3 -0
- package/components/features/{WorkflowBuilder/WorkflowBuilder.behaviors.stories.tsx → NodeEditor/NodeEditor.behaviors.stories.tsx} +6 -6
- package/components/features/{WorkflowBuilder/WorkflowBuilder.mocks.ts → NodeEditor/NodeEditor.mocks.ts} +3 -3
- package/components/features/{WorkflowBuilder/WorkflowBuilder.stories.tsx → NodeEditor/NodeEditor.stories.tsx} +16 -16
- package/components/features/{WorkflowBuilder/WorkflowBuilder.tsx → NodeEditor/NodeEditor.tsx} +3 -3
- package/components/features/NodeEditor/index.ts +4 -0
- package/components/features/{WorkflowBuilder/useWorkflowBuilder.d.ts → NodeEditor/useNodeEditor.d.ts} +4 -4
- package/components/features/{WorkflowBuilder/useWorkflowBuilder.mock.ts → NodeEditor/useNodeEditor.mock.ts} +2 -2
- package/components/features/PageLayout/PageLayout.stories.tsx +3 -3
- package/components/features/PageLayout/PageLayout.tsx +12 -0
- package/components/features/RefinementPanel/RefinementPanel.tsx +36 -0
- package/components/features/{AIDocEditor/AIDocEditor.behaviors.stories.tsx → TextEditor/TextEditor.behaviors.stories.tsx} +6 -6
- package/components/features/{AIDocEditor/AIDocEditor.mocks.ts → TextEditor/TextEditor.mocks.ts} +4 -4
- package/components/features/{AIDocEditor/AIDocEditor.stories.tsx → TextEditor/TextEditor.stories.tsx} +10 -10
- package/components/features/{AIDocEditor/AIDocEditor.tsx → TextEditor/TextEditor.tsx} +72 -46
- package/components/features/TextEditor/index.ts +24 -0
- package/components/features/{AIDocEditor/useAIDocEditor.d.ts → TextEditor/useTextEditor.d.ts} +7 -7
- package/components/features/{AIDocEditor/useAIDocEditor.mock.ts → TextEditor/useTextEditor.mock.ts} +18 -18
- package/components/features/index.ts +6 -6
- package/components/index.ts +2 -2
- package/components/primitives/Breadcrumb/Breadcrumb.stories.tsx +129 -0
- package/components/primitives/Breadcrumb/Breadcrumb.tsx +68 -0
- package/components/primitives/Breadcrumb/index.ts +1 -0
- package/components/primitives/index.ts +1 -0
- package/components/ui/breadcrumb.tsx +109 -0
- package/components/ui/command.tsx +1 -1
- package/dist/index.cjs +569 -92
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +47 -15
- package/dist/index.d.ts +290 -224
- package/dist/index.js +567 -93
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/components/features/AIDocEditor/index.ts +0 -24
- package/components/features/WorkflowBuilder/index.ts +0 -4
- /package/components/features/{WorkflowBuilder → NodeEditor}/README.md +0 -0
- /package/components/features/{AIDocEditor → TextEditor}/README.md +0 -0
|
@@ -10,7 +10,6 @@ import { cn } from "@/lib/utils";
|
|
|
10
10
|
import { BrainIcon, ChevronDownIcon } from "lucide-react";
|
|
11
11
|
import type { ComponentProps } from "react";
|
|
12
12
|
import { createContext, memo, useContext, useEffect, useState } from "react";
|
|
13
|
-
import { Response } from "./response";
|
|
14
13
|
import { Shimmer } from "./shimmer";
|
|
15
14
|
|
|
16
15
|
type ReasoningContextValue = {
|
|
@@ -179,7 +178,7 @@ export const ReasoningContent = memo(
|
|
|
179
178
|
)}
|
|
180
179
|
{...props}
|
|
181
180
|
>
|
|
182
|
-
|
|
181
|
+
{children}
|
|
183
182
|
</CollapsibleContent>
|
|
184
183
|
)
|
|
185
184
|
);
|
|
@@ -101,9 +101,94 @@ export const AIConversation = React.memo<AIConversationProps>(
|
|
|
101
101
|
const toMessageString = (content: unknown): string =>
|
|
102
102
|
typeof content === 'string' ? content : ''
|
|
103
103
|
|
|
104
|
+
const groupedMessages = React.useMemo(() => {
|
|
105
|
+
const result: AIMessage[] = [];
|
|
106
|
+
let currentGroup: AIMessage | null = null;
|
|
107
|
+
|
|
108
|
+
for (const msg of messages) {
|
|
109
|
+
if (msg.role === "orchestrator") {
|
|
110
|
+
const isFinalResponse =
|
|
111
|
+
!msg.isLoading &&
|
|
112
|
+
(!msg.toolCalls || msg.toolCalls.length === 0) &&
|
|
113
|
+
(!msg.subAgents || msg.subAgents.length === 0);
|
|
114
|
+
|
|
115
|
+
if (!isFinalResponse) {
|
|
116
|
+
if (currentGroup) {
|
|
117
|
+
if (msg.content) {
|
|
118
|
+
currentGroup.content = currentGroup.content
|
|
119
|
+
? `${currentGroup.content}\n\n${msg.content}`
|
|
120
|
+
: msg.content;
|
|
121
|
+
}
|
|
122
|
+
if (msg.toolCalls) {
|
|
123
|
+
const existingToolCalls = currentGroup.toolCalls || [];
|
|
124
|
+
const updatedToolCalls = [...existingToolCalls];
|
|
125
|
+
for (const newCall of msg.toolCalls) {
|
|
126
|
+
const existingIndex = updatedToolCalls.findIndex(tc => tc.id === newCall.id);
|
|
127
|
+
if (existingIndex >= 0) {
|
|
128
|
+
updatedToolCalls[existingIndex] = newCall;
|
|
129
|
+
} else {
|
|
130
|
+
updatedToolCalls.push(newCall);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
currentGroup.toolCalls = updatedToolCalls;
|
|
134
|
+
}
|
|
135
|
+
if (msg.subAgents) {
|
|
136
|
+
const existingSubAgents = currentGroup.subAgents || [];
|
|
137
|
+
const updatedSubAgents = [...existingSubAgents];
|
|
138
|
+
for (const newAgent of msg.subAgents) {
|
|
139
|
+
const existingIndex = updatedSubAgents.findIndex(a => a.id === newAgent.id);
|
|
140
|
+
if (existingIndex >= 0) {
|
|
141
|
+
updatedSubAgents[existingIndex] = newAgent;
|
|
142
|
+
} else {
|
|
143
|
+
// Merge consecutive subAgents with the same name
|
|
144
|
+
if (
|
|
145
|
+
updatedSubAgents.length > 0 &&
|
|
146
|
+
updatedSubAgents[updatedSubAgents.length - 1].subAgentName === newAgent.subAgentName
|
|
147
|
+
) {
|
|
148
|
+
const prev = updatedSubAgents[updatedSubAgents.length - 1];
|
|
149
|
+
let mergedOutput = prev.output;
|
|
150
|
+
if (newAgent.output) {
|
|
151
|
+
const prevStr = typeof prev.output === 'string' ? prev.output : (prev.output ? JSON.stringify(prev.output) : '');
|
|
152
|
+
const newStr = typeof newAgent.output === 'string' ? newAgent.output : JSON.stringify(newAgent.output);
|
|
153
|
+
mergedOutput = prevStr ? `${prevStr}\n\n${newStr}` : newStr;
|
|
154
|
+
}
|
|
155
|
+
updatedSubAgents[updatedSubAgents.length - 1] = {
|
|
156
|
+
...newAgent,
|
|
157
|
+
id: prev.id, // keep the original id so React keys don't jump
|
|
158
|
+
input: prev.input, // keep original input or combine? Just keep original
|
|
159
|
+
output: mergedOutput
|
|
160
|
+
};
|
|
161
|
+
} else {
|
|
162
|
+
updatedSubAgents.push(newAgent);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
currentGroup.subAgents = updatedSubAgents;
|
|
167
|
+
}
|
|
168
|
+
currentGroup.isLoading = msg.isLoading;
|
|
169
|
+
} else {
|
|
170
|
+
currentGroup = {
|
|
171
|
+
...msg,
|
|
172
|
+
toolCalls: msg.toolCalls ? [...msg.toolCalls] : undefined,
|
|
173
|
+
subAgents: msg.subAgents ? [...msg.subAgents] : undefined,
|
|
174
|
+
};
|
|
175
|
+
result.push(currentGroup);
|
|
176
|
+
}
|
|
177
|
+
} else {
|
|
178
|
+
currentGroup = null;
|
|
179
|
+
result.push(msg);
|
|
180
|
+
}
|
|
181
|
+
} else {
|
|
182
|
+
currentGroup = null;
|
|
183
|
+
result.push(msg);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return result;
|
|
187
|
+
}, [messages]);
|
|
188
|
+
|
|
104
189
|
const renderedMessages = React.useMemo(
|
|
105
190
|
() =>
|
|
106
|
-
|
|
191
|
+
groupedMessages.map((message, index) => {
|
|
107
192
|
const contentStr = toMessageString(message.content)
|
|
108
193
|
|
|
109
194
|
// Render based on role field
|
|
@@ -137,8 +222,7 @@ export const AIConversation = React.memo<AIConversationProps>(
|
|
|
137
222
|
const reasoningCalls = allToolCalls.filter((tc) => tc.visibility === "reasoning")
|
|
138
223
|
const directToolCalls = allToolCalls.filter((tc) => tc.visibility !== "reasoning")
|
|
139
224
|
|
|
140
|
-
const
|
|
141
|
-
const hasReasoning = reasoningCalls.length > 0
|
|
225
|
+
const hasReasoning = reasoningCalls.length > 0 || subAgents.length > 0 || directToolCalls.length > 0 || (message.isLoading && contentStr.trim() !== "");
|
|
142
226
|
const reasoningText = hasReasoning ? contentStr : undefined
|
|
143
227
|
const displayContentStr = hasReasoning ? "" : contentStr
|
|
144
228
|
const hasDisplayContent = displayContentStr.trim() !== ""
|
|
@@ -147,7 +231,7 @@ export const AIConversation = React.memo<AIConversationProps>(
|
|
|
147
231
|
return null;
|
|
148
232
|
}
|
|
149
233
|
|
|
150
|
-
const isStreaming = reasoningCalls.some((tc) => tc.status === "pending")
|
|
234
|
+
const isStreaming = message.isLoading || reasoningCalls.some((tc) => tc.status === "pending") || subAgents.some(sa => sa.status === "active") || directToolCalls.some(tc => tc.status === "pending")
|
|
151
235
|
|
|
152
236
|
return (
|
|
153
237
|
<OrchestratorMessage
|
|
@@ -161,36 +245,37 @@ export const AIConversation = React.memo<AIConversationProps>(
|
|
|
161
245
|
}}
|
|
162
246
|
showAvatar={showAvatars && hasDisplayContent}
|
|
163
247
|
>
|
|
164
|
-
{/* Render reasoning-section for hidden tool results */}
|
|
248
|
+
{/* Render reasoning-section for hidden tool results and subagents */}
|
|
165
249
|
{hasReasoning && (
|
|
166
250
|
<ReasoningDisplay
|
|
167
251
|
content={reasoningText}
|
|
168
252
|
items={reasoningCalls}
|
|
169
253
|
isStreaming={isStreaming}
|
|
170
254
|
onToolAction={onToolAction}
|
|
171
|
-
|
|
172
|
-
|
|
255
|
+
defaultOpen={isStreaming || index === groupedMessages.length - 1}
|
|
256
|
+
>
|
|
257
|
+
{/* Render direct tool calls */}
|
|
258
|
+
{directToolCalls.map((tc) => (
|
|
259
|
+
<ToolCallDisplay key={tc.id} toolCall={tc} onToolAction={onToolAction} />
|
|
260
|
+
))}
|
|
173
261
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
isNested={true}
|
|
192
|
-
/>
|
|
193
|
-
))}
|
|
262
|
+
{/* Render specialist sub-agents */}
|
|
263
|
+
{subAgents.map((subAgent) => (
|
|
264
|
+
<SpecialistMessage
|
|
265
|
+
key={subAgent.id}
|
|
266
|
+
message={{
|
|
267
|
+
id: subAgent.id,
|
|
268
|
+
name: subAgent.subAgentName,
|
|
269
|
+
description: undefined,
|
|
270
|
+
content: typeof subAgent.output === 'string' ? subAgent.output : (subAgent.output ? JSON.stringify(subAgent.output) : ''),
|
|
271
|
+
status: subAgent.status,
|
|
272
|
+
toolCalls: [],
|
|
273
|
+
}}
|
|
274
|
+
isNested={true}
|
|
275
|
+
/>
|
|
276
|
+
))}
|
|
277
|
+
</ReasoningDisplay>
|
|
278
|
+
)}
|
|
194
279
|
</OrchestratorMessage>
|
|
195
280
|
)
|
|
196
281
|
}
|
|
@@ -210,7 +295,7 @@ export const AIConversation = React.memo<AIConversationProps>(
|
|
|
210
295
|
/>
|
|
211
296
|
)
|
|
212
297
|
}),
|
|
213
|
-
[
|
|
298
|
+
[groupedMessages, showAvatars, onToolAction]
|
|
214
299
|
)
|
|
215
300
|
|
|
216
301
|
return (
|
|
@@ -17,6 +17,7 @@ export interface FormReportsSectionProps {
|
|
|
17
17
|
pagination?: DashboardPaginationState
|
|
18
18
|
tableHandlers?: FormReportsTableHandlers
|
|
19
19
|
tableLeftActions?: React.ReactNode
|
|
20
|
+
enableRowSelection?: boolean
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
export const FormReportsSection = React.memo<FormReportsSectionProps>(
|
|
@@ -29,6 +30,7 @@ export const FormReportsSection = React.memo<FormReportsSectionProps>(
|
|
|
29
30
|
pagination,
|
|
30
31
|
tableHandlers,
|
|
31
32
|
tableLeftActions,
|
|
33
|
+
enableRowSelection,
|
|
32
34
|
}) => {
|
|
33
35
|
return (
|
|
34
36
|
<section>
|
|
@@ -41,6 +43,7 @@ export const FormReportsSection = React.memo<FormReportsSectionProps>(
|
|
|
41
43
|
leftActions={tableLeftActions}
|
|
42
44
|
onCreateClick={onCreateClick}
|
|
43
45
|
createButtonLabel={createButtonLabel}
|
|
46
|
+
enableRowSelection={enableRowSelection}
|
|
44
47
|
/>
|
|
45
48
|
</section>
|
|
46
49
|
)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { AppBreadcrumb } from "./AppBreadcrumb";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* AppBreadcrumb Composite Stories
|
|
6
|
+
*
|
|
7
|
+
* The AppBreadcrumb provides an easy way to render breadcrumbs by passing
|
|
8
|
+
* an array of data, automatically handling separators and active states.
|
|
9
|
+
*/
|
|
10
|
+
const meta = {
|
|
11
|
+
title: "Composites/AppBreadcrumb",
|
|
12
|
+
component: AppBreadcrumb,
|
|
13
|
+
tags: ["autodocs"],
|
|
14
|
+
parameters: { layout: "padded" },
|
|
15
|
+
} satisfies Meta<typeof AppBreadcrumb>;
|
|
16
|
+
|
|
17
|
+
export default meta;
|
|
18
|
+
type Story = StoryObj<typeof meta>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Default AppBreadcrumb usage
|
|
22
|
+
*/
|
|
23
|
+
export const Default: Story = {
|
|
24
|
+
args: {
|
|
25
|
+
items: [
|
|
26
|
+
{ label: "Home", href: "/" },
|
|
27
|
+
{ label: "Workflows", href: "/workflows" },
|
|
28
|
+
{ label: "My Workflow" },
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* With an ellipsis
|
|
35
|
+
*/
|
|
36
|
+
export const WithEllipsis: Story = {
|
|
37
|
+
args: {
|
|
38
|
+
items: [
|
|
39
|
+
{ label: "Home", href: "/" },
|
|
40
|
+
{ label: "Folders", href: "/folders" },
|
|
41
|
+
{ label: "Subfolder", href: "/folders/1" },
|
|
42
|
+
{ label: "Document" },
|
|
43
|
+
],
|
|
44
|
+
showEllipsis: true,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import {
|
|
3
|
+
Breadcrumb,
|
|
4
|
+
BreadcrumbList,
|
|
5
|
+
BreadcrumbItem,
|
|
6
|
+
BreadcrumbLink,
|
|
7
|
+
BreadcrumbPage,
|
|
8
|
+
BreadcrumbSeparator,
|
|
9
|
+
BreadcrumbEllipsis
|
|
10
|
+
} from "@/components/primitives/Breadcrumb";
|
|
11
|
+
|
|
12
|
+
export interface BreadcrumbItemData {
|
|
13
|
+
label: React.ReactNode;
|
|
14
|
+
href?: string;
|
|
15
|
+
isCurrentPage?: boolean;
|
|
16
|
+
onClick?: (e: React.MouseEvent<HTMLAnchorElement>) => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface AppBreadcrumbProps extends React.HTMLAttributes<HTMLElement> {
|
|
20
|
+
items: BreadcrumbItemData[];
|
|
21
|
+
showEllipsis?: boolean;
|
|
22
|
+
ellipsisIndex?: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* AppBreadcrumb Composite
|
|
27
|
+
*
|
|
28
|
+
* A composite wrapper around the Breadcrumb primitive that accepts a data array
|
|
29
|
+
* and handles the complex rendering of links, separators, and ellipses.
|
|
30
|
+
*/
|
|
31
|
+
export const AppBreadcrumb = React.forwardRef<HTMLElement, AppBreadcrumbProps>(
|
|
32
|
+
({ items, showEllipsis = false, ellipsisIndex = 1, className, ...props }, ref) => {
|
|
33
|
+
if (!items || items.length === 0) return null;
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<Breadcrumb ref={ref} className={className} {...props}>
|
|
37
|
+
<BreadcrumbList>
|
|
38
|
+
{items.map((item, index) => {
|
|
39
|
+
const isLast = index === items.length - 1;
|
|
40
|
+
const isEllipsis = showEllipsis && index === ellipsisIndex && items.length > 3;
|
|
41
|
+
|
|
42
|
+
if (isEllipsis) {
|
|
43
|
+
return (
|
|
44
|
+
<React.Fragment key={`ellipsis-${index}`}>
|
|
45
|
+
<BreadcrumbItem>
|
|
46
|
+
<BreadcrumbEllipsis />
|
|
47
|
+
</BreadcrumbItem>
|
|
48
|
+
{!isLast && <BreadcrumbSeparator />}
|
|
49
|
+
</React.Fragment>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<React.Fragment key={`item-${index}`}>
|
|
55
|
+
<BreadcrumbItem>
|
|
56
|
+
{item.isCurrentPage || isLast ? (
|
|
57
|
+
<BreadcrumbPage>{item.label}</BreadcrumbPage>
|
|
58
|
+
) : (
|
|
59
|
+
<BreadcrumbLink
|
|
60
|
+
href={item.href || "#"}
|
|
61
|
+
onClick={item.onClick}
|
|
62
|
+
>
|
|
63
|
+
{item.label}
|
|
64
|
+
</BreadcrumbLink>
|
|
65
|
+
)}
|
|
66
|
+
</BreadcrumbItem>
|
|
67
|
+
{!isLast && <BreadcrumbSeparator />}
|
|
68
|
+
</React.Fragment>
|
|
69
|
+
);
|
|
70
|
+
})}
|
|
71
|
+
</BreadcrumbList>
|
|
72
|
+
</Breadcrumb>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
AppBreadcrumb.displayName = "AppBreadcrumb";
|
|
@@ -8,9 +8,11 @@ import type { DashboardRow } from "./table-types"
|
|
|
8
8
|
export interface DraggableRowProps {
|
|
9
9
|
row: Row<DashboardRow>
|
|
10
10
|
rowId: number | string
|
|
11
|
+
onClick?: (row: DashboardRow) => void
|
|
12
|
+
className?: string
|
|
11
13
|
}
|
|
12
14
|
|
|
13
|
-
export function DraggableRow({ row, rowId }: DraggableRowProps) {
|
|
15
|
+
export function DraggableRow({ row, rowId, onClick, className }: DraggableRowProps) {
|
|
14
16
|
const { transform, transition, setNodeRef, isDragging } = useSortable({
|
|
15
17
|
id: rowId,
|
|
16
18
|
})
|
|
@@ -21,7 +23,8 @@ export function DraggableRow({ row, rowId }: DraggableRowProps) {
|
|
|
21
23
|
data-row-id={String(rowId)}
|
|
22
24
|
data-state={row.getIsSelected() && "selected"}
|
|
23
25
|
data-dragging={isDragging}
|
|
24
|
-
|
|
26
|
+
onClick={() => onClick?.(row.original)}
|
|
27
|
+
className={`relative z-0 data-[dragging=true]:z-10 data-[dragging=true]:opacity-80 ${className ?? ""}`}
|
|
25
28
|
style={{
|
|
26
29
|
transform: CSS.Transform.toString(transform),
|
|
27
30
|
transition,
|
|
@@ -539,7 +539,13 @@ export function EnhancedDataTable({
|
|
|
539
539
|
{table.getRowModel().rows?.length ? (
|
|
540
540
|
<SortableContext items={dataIds} strategy={verticalListSortingStrategy}>
|
|
541
541
|
{table.getRowModel().rows.map((row) => (
|
|
542
|
-
<DraggableRow
|
|
542
|
+
<DraggableRow
|
|
543
|
+
key={row.id}
|
|
544
|
+
row={row}
|
|
545
|
+
rowId={toRowId(row.original, tableSchema)}
|
|
546
|
+
onClick={handlers?.onRowClick}
|
|
547
|
+
className={handlers?.onRowClick ? "cursor-pointer hover:bg-muted/50" : ""}
|
|
548
|
+
/>
|
|
543
549
|
))}
|
|
544
550
|
</SortableContext>
|
|
545
551
|
) : (
|
|
@@ -38,6 +38,7 @@ export interface FormReportsTableHandlers {
|
|
|
38
38
|
onPaginationChange?: (pageIndex: number, pageSize: number) => void
|
|
39
39
|
onPageSizeChange?: (pageSize: number) => void
|
|
40
40
|
onPageChange?: (pageIndex: number) => void
|
|
41
|
+
onRowClick?: (row: FormReportsEntity) => void
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
export interface FormReportsTableProps {
|
|
@@ -50,6 +51,7 @@ export interface FormReportsTableProps {
|
|
|
50
51
|
rightActions?: React.ReactNode
|
|
51
52
|
onCreateClick?: () => void
|
|
52
53
|
createButtonLabel?: string
|
|
54
|
+
enableRowSelection?: boolean
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
const dashboardToFormReportsActionMap: Record<DashboardRowAction, string> = {
|
|
@@ -62,7 +64,7 @@ const dashboardToFormReportsActionMap: Record<DashboardRowAction, string> = {
|
|
|
62
64
|
export type { DashboardPaginationState }
|
|
63
65
|
|
|
64
66
|
export const FormReportsTable = React.memo<FormReportsTableProps>(
|
|
65
|
-
({ items, columns, pagination, handlers, leftActions, rightActions, onCreateClick, createButtonLabel }) => {
|
|
67
|
+
({ items, columns, pagination, handlers, leftActions, rightActions, onCreateClick, createButtonLabel, enableRowSelection }) => {
|
|
66
68
|
const { rows, originalById, tableSchema } = React.useMemo(() => {
|
|
67
69
|
const byId = new Map<string, FormReportsEntity>()
|
|
68
70
|
const tableColumns: DynamicTableSchema["columns"] = columns
|
|
@@ -87,10 +89,10 @@ export const FormReportsTable = React.memo<FormReportsTableProps>(
|
|
|
87
89
|
columns: tableColumns,
|
|
88
90
|
enableFiltering: true,
|
|
89
91
|
enablePagination: true,
|
|
90
|
-
enableRowSelection: true,
|
|
92
|
+
enableRowSelection: enableRowSelection ?? true,
|
|
91
93
|
}),
|
|
92
94
|
}
|
|
93
|
-
}, [columns, items])
|
|
95
|
+
}, [columns, items, enableRowSelection])
|
|
94
96
|
|
|
95
97
|
const adaptedHandlers = React.useMemo<DashboardTableActionHandlers>(
|
|
96
98
|
() => ({
|
|
@@ -164,6 +166,11 @@ export const FormReportsTable = React.memo<FormReportsTableProps>(
|
|
|
164
166
|
onPaginationChange: (pageIndex, pageSize) => handlers?.onPaginationChange?.(pageIndex, pageSize),
|
|
165
167
|
onPageSizeChange: (pageSize) => handlers?.onPageSizeChange?.(pageSize),
|
|
166
168
|
onPageChange: (pageIndex) => handlers?.onPageChange?.(pageIndex),
|
|
169
|
+
onRowClick: (row) => {
|
|
170
|
+
const originalRow = originalById.get(String(row.id))
|
|
171
|
+
if (!originalRow) return
|
|
172
|
+
handlers?.onRowClick?.(originalRow)
|
|
173
|
+
}
|
|
167
174
|
}),
|
|
168
175
|
[handlers, originalById]
|
|
169
176
|
)
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
MessageAvatar,
|
|
11
11
|
MessageTypingIndicator,
|
|
12
12
|
} from "@/components/ai-elements/message"
|
|
13
|
-
import { Response } from "@/components/
|
|
13
|
+
import { Response } from "@/components/composites/response"
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* OrchestratorMessage Block
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import * as React from "react"
|
|
2
2
|
import { Icon } from "@/components/primitives/Icon"
|
|
3
|
+
import { Command as CommandPrimitive } from "cmdk"
|
|
3
4
|
|
|
4
5
|
import { Button } from "@/components/primitives/Button"
|
|
5
6
|
import {
|
|
6
7
|
Command,
|
|
7
8
|
CommandEmpty,
|
|
8
9
|
CommandGroup,
|
|
9
|
-
CommandInput,
|
|
10
10
|
CommandItem,
|
|
11
11
|
CommandList,
|
|
12
12
|
CommandSeparator,
|
|
@@ -37,6 +37,8 @@ export const ProjectSwitcher = React.memo<ProjectSwitcherProps>(
|
|
|
37
37
|
const selectedProject = projects.find((p) => p.id === selectedProjectId)
|
|
38
38
|
const displayLabel = selectedProject ? selectedProject.name : "Select Project..."
|
|
39
39
|
|
|
40
|
+
const defaultValue = selectedProject ? `${selectedProject.name}-${selectedProject.id}` : undefined
|
|
41
|
+
|
|
40
42
|
return (
|
|
41
43
|
<Popover open={open} onOpenChange={setOpen}>
|
|
42
44
|
<PopoverTrigger asChild>
|
|
@@ -46,13 +48,21 @@ export const ProjectSwitcher = React.memo<ProjectSwitcherProps>(
|
|
|
46
48
|
aria-expanded={open}
|
|
47
49
|
className={`w-[240px] justify-between font-medium ${className || ""}`}
|
|
48
50
|
>
|
|
49
|
-
|
|
51
|
+
<div className="flex items-center">
|
|
52
|
+
{displayLabel}
|
|
53
|
+
</div>
|
|
50
54
|
<Icon name="chevrons-up-down" size="sm" className="ml-2 shrink-0 opacity-50" />
|
|
51
55
|
</Button>
|
|
52
56
|
</PopoverTrigger>
|
|
53
57
|
<PopoverContent className="w-[240px] p-0" align="start">
|
|
54
|
-
<Command>
|
|
55
|
-
<
|
|
58
|
+
<Command defaultValue={defaultValue}>
|
|
59
|
+
<div className="flex items-center justify-between border-b border-neutral-600 px-3" cmdk-input-wrapper="">
|
|
60
|
+
<CommandPrimitive.Input
|
|
61
|
+
placeholder="Find Project..."
|
|
62
|
+
className="flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50"
|
|
63
|
+
/>
|
|
64
|
+
<kbd className="pointer-events-none inline-flex h-5 select-none items-center gap-1 rounded border border-neutral-700 bg-neutral-800 px-1.5 font-mono text-[10px] font-medium text-muted-foreground opacity-100">Esc</kbd>
|
|
65
|
+
</div>
|
|
56
66
|
<CommandList className="max-h-[300px]">
|
|
57
67
|
<CommandEmpty>
|
|
58
68
|
<div className="flex flex-col items-center justify-center p-6 text-center text-sm text-muted-foreground">
|
|
@@ -60,22 +70,25 @@ export const ProjectSwitcher = React.memo<ProjectSwitcherProps>(
|
|
|
60
70
|
</div>
|
|
61
71
|
</CommandEmpty>
|
|
62
72
|
{projects.length > 0 && (
|
|
63
|
-
<CommandGroup
|
|
73
|
+
<CommandGroup>
|
|
64
74
|
{projects.map((project) => (
|
|
65
75
|
<CommandItem
|
|
66
76
|
key={project.id}
|
|
67
|
-
value={project.name}
|
|
77
|
+
value={`${project.name}-${project.id}`}
|
|
68
78
|
onSelect={() => {
|
|
69
79
|
onSelectProject(project.id)
|
|
70
80
|
setOpen(false)
|
|
71
81
|
}}
|
|
82
|
+
className="mb-1 last:mb-0 cursor-pointer flex items-center justify-between"
|
|
72
83
|
>
|
|
84
|
+
<div className="flex items-center">
|
|
85
|
+
{project.name}
|
|
86
|
+
</div>
|
|
73
87
|
<Icon name="check"
|
|
74
|
-
className={`
|
|
88
|
+
className={`h-4 w-4 ${
|
|
75
89
|
selectedProjectId === project.id ? "opacity-100" : "opacity-0"
|
|
76
90
|
}`}
|
|
77
91
|
/>
|
|
78
|
-
{project.name}
|
|
79
92
|
</CommandItem>
|
|
80
93
|
))}
|
|
81
94
|
</CommandGroup>
|
|
@@ -91,8 +104,8 @@ export const ProjectSwitcher = React.memo<ProjectSwitcherProps>(
|
|
|
91
104
|
onCreateProject()
|
|
92
105
|
}}
|
|
93
106
|
>
|
|
94
|
-
<Icon name="plus" size="sm" className="mr-2" />
|
|
95
|
-
Create
|
|
107
|
+
<Icon name="plus" size="sm" className="mr-2 h-4 w-4" />
|
|
108
|
+
Create Project
|
|
96
109
|
</Button>
|
|
97
110
|
</div>
|
|
98
111
|
</Command>
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
ReasoningTrigger,
|
|
6
6
|
} from "@/components/ai-elements/reasoning";
|
|
7
7
|
import { CollapsibleContent } from "@/components/primitives/Collapsible";
|
|
8
|
-
import { Response } from "@/components/
|
|
8
|
+
import { Response } from "@/components/composites/response";
|
|
9
9
|
import { ToolCallDisplay } from "@/components/composites/ToolCallDisplay";
|
|
10
10
|
import type { ToolCall } from "@/components/composites/ToolCallDisplay";
|
|
11
11
|
|
|
@@ -14,6 +14,8 @@ export interface ReasoningDisplayProps {
|
|
|
14
14
|
isStreaming?: boolean;
|
|
15
15
|
content?: string;
|
|
16
16
|
onToolAction?: (toolCall: ToolCall, action: string) => void;
|
|
17
|
+
defaultOpen?: boolean;
|
|
18
|
+
children?: React.ReactNode;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
export const ReasoningDisplay = ({
|
|
@@ -21,11 +23,13 @@ export const ReasoningDisplay = ({
|
|
|
21
23
|
isStreaming = false,
|
|
22
24
|
content,
|
|
23
25
|
onToolAction,
|
|
26
|
+
defaultOpen = false,
|
|
27
|
+
children,
|
|
24
28
|
}: ReasoningDisplayProps) => {
|
|
25
|
-
if (items.length === 0 && !content) return null;
|
|
29
|
+
if (items.length === 0 && !content && !children) return null;
|
|
26
30
|
|
|
27
31
|
return (
|
|
28
|
-
<Reasoning isStreaming={isStreaming} defaultOpen={
|
|
32
|
+
<Reasoning isStreaming={isStreaming} defaultOpen={defaultOpen}>
|
|
29
33
|
<ReasoningTrigger />
|
|
30
34
|
<CollapsibleContent
|
|
31
35
|
className="mt-4 flex flex-col gap-2 data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 text-muted-foreground outline-none data-[state=closed]:animate-out data-[state=open]:animate-in"
|
|
@@ -39,6 +43,7 @@ export const ReasoningDisplay = ({
|
|
|
39
43
|
{items.map((item) => (
|
|
40
44
|
<ToolCallDisplay key={item.id} toolCall={item} onToolAction={onToolAction} />
|
|
41
45
|
))}
|
|
46
|
+
{children}
|
|
42
47
|
</div>
|
|
43
48
|
</CollapsibleContent>
|
|
44
49
|
</Reasoning>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { SessionHeader } from "./SessionHeader";
|
|
3
|
+
import { fn } from "@storybook/test";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* SessionHeader Composite Stories
|
|
7
|
+
*
|
|
8
|
+
* The SessionHeader displays the current active session title and provides
|
|
9
|
+
* actions to start a new session or view previous sessions.
|
|
10
|
+
*/
|
|
11
|
+
const meta = {
|
|
12
|
+
title: "Composites/SessionHeader",
|
|
13
|
+
component: SessionHeader,
|
|
14
|
+
tags: ["autodocs"],
|
|
15
|
+
parameters: { layout: "padded" },
|
|
16
|
+
args: {
|
|
17
|
+
onNewSession: fn(),
|
|
18
|
+
onCloseSession: fn(),
|
|
19
|
+
onSelectSession: fn(),
|
|
20
|
+
}
|
|
21
|
+
} satisfies Meta<typeof SessionHeader>;
|
|
22
|
+
|
|
23
|
+
export default meta;
|
|
24
|
+
type Story = StoryObj<typeof meta>;
|
|
25
|
+
|
|
26
|
+
const mockSessions = [
|
|
27
|
+
{ id: "1", title: "Debug Authentication Flow", created_at: "2026-07-01T10:00:00Z" },
|
|
28
|
+
{ id: "2", title: "Refactor Database Queries", created_at: "2026-07-02T14:30:00Z" },
|
|
29
|
+
{ id: "3", title: "Update Dependencies", created_at: "2026-07-03T09:15:00Z" },
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Default usage with an active session
|
|
34
|
+
*/
|
|
35
|
+
export const Default: Story = {
|
|
36
|
+
args: {
|
|
37
|
+
activeSessionId: "1",
|
|
38
|
+
sessions: mockSessions,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Usage when there are no active or past sessions
|
|
44
|
+
*/
|
|
45
|
+
export const EmptyState: Story = {
|
|
46
|
+
args: {
|
|
47
|
+
activeSessionId: null,
|
|
48
|
+
sessions: [],
|
|
49
|
+
},
|
|
50
|
+
};
|