ai-design-system 0.1.73 → 0.1.74
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/node.tsx +9 -9
- package/components/ai-elements/prompt-input.tsx +2 -2
- package/components/blocks/AIConversation/AIConversation.tsx +36 -3
- package/components/blocks/BmcCanvas/bmc-layout.ts +115 -0
- package/components/blocks/BmcCanvas/bmc-tooltips.ts +63 -0
- package/components/blocks/BmcCanvas/index.ts +3 -0
- package/components/blocks/BmcCanvas/interfaces.ts +58 -0
- package/components/blocks/WorkflowCanvas/WorkflowCanvas.tsx +1 -1
- package/components/blocks/index.ts +3 -0
- package/components/composites/AgentIndicator/AgentIndicator.tsx +1 -1
- package/components/composites/SpatialContainerNode/SpatialContainerNode.tsx +84 -21
- package/components/composites/SpecialistMessage/SpecialistMessage.tsx +1 -1
- package/components/composites/SystemMessage/SystemMessage.stories.tsx +46 -0
- package/components/composites/SystemMessage/SystemMessage.tsx +54 -0
- package/components/composites/SystemMessage/index.ts +2 -0
- package/components/composites/ToolCallDisplay/ToolCallDisplay.tsx +1 -1
- package/components/composites/WorkflowToolbar/WorkflowToolbar.tsx +8 -9
- package/components/composites/WorkflowToolbar/interfaces.ts +1 -0
- package/components/composites/index.ts +4 -0
- package/components/features/ChatPanel/ChatPanel.tsx +18 -2
- package/components/features/ChatPanel/useChatPanel.d.ts +1 -1
- package/components/features/NodeEditor/NodeEditor.mocks.ts +51 -123
- package/components/features/NodeEditor/NodeEditor.stories.tsx +1 -1
- package/components/features/NodeEditor/NodeEditor.tsx +13 -6
- package/components/features/NodeEditor/useNodeEditor.d.ts +1 -0
- package/components/features/NodeEditor/useNodeEditor.mock.ts +5 -0
- package/components/index.ts +1 -1
- package/components/ui/button.tsx +2 -0
- package/dist/index.cjs +285 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +202 -23
- package/dist/index.d.ts +117 -21
- package/dist/index.js +285 -45
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -23,7 +23,7 @@ export type NodeProps = ComponentProps<typeof Card> & {
|
|
|
23
23
|
export const Node = ({ handles, className, status, ...props }: NodeProps) => (
|
|
24
24
|
<Card
|
|
25
25
|
className={cn(
|
|
26
|
-
"node-container relative h-[52px] w-[180px] gap-0 overflow-hidden rounded-md bg-card p-0 transition-all duration-200",
|
|
26
|
+
"node-container group relative h-[52px] w-[180px] gap-0 overflow-hidden rounded-md bg-card p-0 transition-all duration-200",
|
|
27
27
|
status === "success" && "border-green-500 border-2",
|
|
28
28
|
status === "error" && "border-red-500 border-2",
|
|
29
29
|
className
|
|
@@ -33,18 +33,18 @@ export const Node = ({ handles, className, status, ...props }: NodeProps) => (
|
|
|
33
33
|
{status === "running" && <AnimatedBorder />}
|
|
34
34
|
{handles.target && (
|
|
35
35
|
<>
|
|
36
|
-
<Handle id="target-top" position={Position.Top} type="target" />
|
|
37
|
-
<Handle id="target-right" position={Position.Right} type="target" />
|
|
38
|
-
<Handle id="target-bottom" position={Position.Bottom} type="target" />
|
|
39
|
-
<Handle id="target-left" position={Position.Left} type="target" />
|
|
36
|
+
<Handle id="target-top" position={Position.Top} type="target" className="opacity-0 group-hover:opacity-100 group-[.selected]:opacity-100 transition-opacity duration-150" />
|
|
37
|
+
<Handle id="target-right" position={Position.Right} type="target" className="opacity-0 group-hover:opacity-100 group-[.selected]:opacity-100 transition-opacity duration-150" />
|
|
38
|
+
<Handle id="target-bottom" position={Position.Bottom} type="target" className="opacity-0 group-hover:opacity-100 group-[.selected]:opacity-100 transition-opacity duration-150" />
|
|
39
|
+
<Handle id="target-left" position={Position.Left} type="target" className="opacity-0 group-hover:opacity-100 group-[.selected]:opacity-100 transition-opacity duration-150" />
|
|
40
40
|
</>
|
|
41
41
|
)}
|
|
42
42
|
{handles.source && (
|
|
43
43
|
<>
|
|
44
|
-
<Handle id="source-top" position={Position.Top} type="source" />
|
|
45
|
-
<Handle id="source-right" position={Position.Right} type="source" />
|
|
46
|
-
<Handle id="source-bottom" position={Position.Bottom} type="source" />
|
|
47
|
-
<Handle id="source-left" position={Position.Left} type="source" />
|
|
44
|
+
<Handle id="source-top" position={Position.Top} type="source" className="opacity-0 group-hover:opacity-100 group-[.selected]:opacity-100 transition-opacity duration-150" />
|
|
45
|
+
<Handle id="source-right" position={Position.Right} type="source" className="opacity-0 group-hover:opacity-100 group-[.selected]:opacity-100 transition-opacity duration-150" />
|
|
46
|
+
<Handle id="source-bottom" position={Position.Bottom} type="source" className="opacity-0 group-hover:opacity-100 group-[.selected]:opacity-100 transition-opacity duration-150" />
|
|
47
|
+
<Handle id="source-left" position={Position.Left} type="source" className="opacity-0 group-hover:opacity-100 group-[.selected]:opacity-100 transition-opacity duration-150" />
|
|
48
48
|
</>
|
|
49
49
|
)}
|
|
50
50
|
{props.children}
|
|
@@ -969,8 +969,8 @@ export const PromptInputSubmit = ({
|
|
|
969
969
|
if (status === "submitted") {
|
|
970
970
|
Icon = <Loader2Icon className="size-4 animate-spin" />;
|
|
971
971
|
} else if (status === "streaming") {
|
|
972
|
-
Icon = <SquareIcon className="size-3.5 fill-
|
|
973
|
-
buttonVariant = "
|
|
972
|
+
Icon = <SquareIcon className="size-3.5 fill-white text-white" />;
|
|
973
|
+
buttonVariant = "outline-primary";
|
|
974
974
|
customClass = cn("rounded-full", className);
|
|
975
975
|
} else if (status === "error") {
|
|
976
976
|
Icon = <XIcon className="size-4" />;
|
|
@@ -10,6 +10,7 @@ import type { ToolCall, SubAgent } from "@/components/composites"
|
|
|
10
10
|
import { UserMessage } from "@/components/composites/UserMessage"
|
|
11
11
|
import { SpecialistMessage } from "@/components/composites/SpecialistMessage"
|
|
12
12
|
import { OrchestratorMessage } from "@/components/composites/OrchestratorMessage"
|
|
13
|
+
import { SystemMessage } from "@/components/composites/SystemMessage"
|
|
13
14
|
import { ToolCallDisplay } from "@/components/composites/ToolCallDisplay"
|
|
14
15
|
import { ReasoningDisplay } from "@/components/composites/ReasoningDisplay"
|
|
15
16
|
|
|
@@ -71,6 +72,11 @@ export interface AIConversationProps
|
|
|
71
72
|
* Callback fired when a tool action (like a link click) is triggered
|
|
72
73
|
*/
|
|
73
74
|
onToolAction?: (toolCall: ToolCall, action: string) => void
|
|
75
|
+
/**
|
|
76
|
+
* Custom renderer for system messages.
|
|
77
|
+
* Receives the raw content string and returns a ReactNode.
|
|
78
|
+
*/
|
|
79
|
+
renderSystemMessage?: (content: string) => React.ReactNode
|
|
74
80
|
}
|
|
75
81
|
|
|
76
82
|
/**
|
|
@@ -84,6 +90,7 @@ export const AIConversation = React.memo<AIConversationProps>(
|
|
|
84
90
|
selectedSubAgent,
|
|
85
91
|
emptyState,
|
|
86
92
|
onToolAction,
|
|
93
|
+
renderSystemMessage,
|
|
87
94
|
...conversationProps
|
|
88
95
|
}) => {
|
|
89
96
|
const isEmpty = React.useMemo(
|
|
@@ -117,6 +124,15 @@ export const AIConversation = React.memo<AIConversationProps>(
|
|
|
117
124
|
(!msg.toolCalls || msg.toolCalls.length === 0) &&
|
|
118
125
|
(!msg.subAgents || msg.subAgents.length === 0);
|
|
119
126
|
|
|
127
|
+
const currentGroupHasTools =
|
|
128
|
+
currentGroup &&
|
|
129
|
+
((currentGroup.toolCalls && currentGroup.toolCalls.length > 0) ||
|
|
130
|
+
(currentGroup.subAgents && currentGroup.subAgents.length > 0));
|
|
131
|
+
|
|
132
|
+
if (currentGroupHasTools && isFinalResponse) {
|
|
133
|
+
currentGroup = null;
|
|
134
|
+
}
|
|
135
|
+
|
|
120
136
|
if (currentGroup) {
|
|
121
137
|
if (msg.content) {
|
|
122
138
|
currentGroup.content = currentGroup.content
|
|
@@ -221,6 +237,23 @@ export const AIConversation = React.memo<AIConversationProps>(
|
|
|
221
237
|
const contentStr = toMessageString(message.content)
|
|
222
238
|
|
|
223
239
|
// Render based on role field
|
|
240
|
+
if (message.role === "system") {
|
|
241
|
+
if (!contentStr || !contentStr.trim()) {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
return (
|
|
245
|
+
<SystemMessage
|
|
246
|
+
key={message.id}
|
|
247
|
+
message={{
|
|
248
|
+
id: message.id,
|
|
249
|
+
content: contentStr,
|
|
250
|
+
avatarName: message.avatarName,
|
|
251
|
+
}}
|
|
252
|
+
renderContent={renderSystemMessage}
|
|
253
|
+
/>
|
|
254
|
+
)
|
|
255
|
+
}
|
|
256
|
+
|
|
224
257
|
if (message.role === "user") {
|
|
225
258
|
return (
|
|
226
259
|
<UserMessage
|
|
@@ -252,9 +285,9 @@ export const AIConversation = React.memo<AIConversationProps>(
|
|
|
252
285
|
const directToolCalls = allToolCalls.filter((tc) => tc.visibility !== "reasoning")
|
|
253
286
|
|
|
254
287
|
const hasReasoning = reasoningCalls.length > 0 || subAgents.length > 0 || directToolCalls.length > 0 || (message.isLoading && contentStr.trim() !== "");
|
|
255
|
-
const reasoningText = (hasReasoning && (!message.blocks || message.blocks.length === 0)) ? contentStr : undefined
|
|
256
|
-
const displayContentStr = hasReasoning ? "" : contentStr
|
|
257
|
-
const hasDisplayContent = displayContentStr.trim() !== ""
|
|
288
|
+
const reasoningText = (hasReasoning && (!message.blocks || message.blocks.length === 0)) ? contentStr : undefined;
|
|
289
|
+
const displayContentStr = hasReasoning ? "" : contentStr;
|
|
290
|
+
const hasDisplayContent = displayContentStr.trim() !== "";
|
|
258
291
|
|
|
259
292
|
if (!hasDisplayContent && directToolCalls.length === 0 && reasoningCalls.length === 0 && subAgents.length === 0 && !message.isLoading && (!message.blocks || message.blocks.length === 0)) {
|
|
260
293
|
return null;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { WorkflowNode, WorkflowEdge } from "@/components/blocks/WorkflowCanvas";
|
|
2
|
+
import type { SpatialContainerColorTheme, SpatialContainerVariant } from "@/components/composites/SpatialContainerNode";
|
|
3
|
+
import type { BmcBlock, BmcBlockId, BmcCanvasView, CompiledBmc } from "./interfaces";
|
|
4
|
+
|
|
5
|
+
interface BlockLayout {
|
|
6
|
+
id: string;
|
|
7
|
+
themeColor: SpatialContainerColorTheme;
|
|
8
|
+
variant: SpatialContainerVariant;
|
|
9
|
+
icon: string;
|
|
10
|
+
position: { x: number; y: number };
|
|
11
|
+
size: { width: number; height: number };
|
|
12
|
+
layout: 'column' | 'row';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const BLOCK_LAYOUTS: Record<BmcBlockId, BlockLayout> = {
|
|
16
|
+
key_partnerships: { id: 'cp', themeColor: 'blue', variant: 'extruded3d', icon: 'handshake', position: { x: 40, y: 40 }, size: { width: 280, height: 400 }, layout: 'column' },
|
|
17
|
+
key_activities: { id: 'ka', themeColor: 'emerald', variant: 'extruded3d', icon: 'activity', position: { x: 350, y: 40 }, size: { width: 280, height: 190 }, layout: 'column' },
|
|
18
|
+
key_resources: { id: 'kr', themeColor: 'emerald', variant: 'extruded3d', icon: 'database', position: { x: 350, y: 250 }, size: { width: 280, height: 190 }, layout: 'column' },
|
|
19
|
+
value_propositions: { id: 'vp', themeColor: 'indigo', variant: 'extruded3d', icon: 'box', position: { x: 660, y: 40 }, size: { width: 300, height: 400 }, layout: 'column' },
|
|
20
|
+
customer_relationships: { id: 'cr', themeColor: 'purple', variant: 'extruded3d', icon: 'heart', position: { x: 990, y: 40 }, size: { width: 300, height: 190 }, layout: 'column' },
|
|
21
|
+
channels: { id: 'ch', themeColor: 'amber', variant: 'extruded3d', icon: 'truck', position: { x: 990, y: 250 }, size: { width: 300, height: 190 }, layout: 'column' },
|
|
22
|
+
customer_segments: { id: 'cs', themeColor: 'purple', variant: 'extruded3d', icon: 'users', position: { x: 1320, y: 40 }, size: { width: 280, height: 400 }, layout: 'column' },
|
|
23
|
+
cost_structure: { id: 'cost', themeColor: 'rose', variant: 'extruded3d', icon: 'wallet', position: { x: 40, y: 465 }, size: { width: 760, height: 180 }, layout: 'row' },
|
|
24
|
+
revenue_streams: { id: 'rev', themeColor: 'cyan', variant: 'extruded3d', icon: 'wallet', position: { x: 840, y: 465 }, size: { width: 760, height: 180 }, layout: 'row' },
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function blockEntries(bmc: CompiledBmc): Array<{ block: BmcBlock; layout: BlockLayout }> {
|
|
28
|
+
return [
|
|
29
|
+
{ block: bmc.keyPartnerships, layout: BLOCK_LAYOUTS.key_partnerships },
|
|
30
|
+
{ block: bmc.keyActivities, layout: BLOCK_LAYOUTS.key_activities },
|
|
31
|
+
{ block: bmc.keyResources, layout: BLOCK_LAYOUTS.key_resources },
|
|
32
|
+
{ block: bmc.valuePropositions, layout: BLOCK_LAYOUTS.value_propositions },
|
|
33
|
+
{ block: bmc.customerRelationships, layout: BLOCK_LAYOUTS.customer_relationships },
|
|
34
|
+
{ block: bmc.channels, layout: BLOCK_LAYOUTS.channels },
|
|
35
|
+
{ block: bmc.customerSegments, layout: BLOCK_LAYOUTS.customer_segments },
|
|
36
|
+
{ block: bmc.costStructure, layout: BLOCK_LAYOUTS.cost_structure },
|
|
37
|
+
{ block: bmc.revenueStreams, layout: BLOCK_LAYOUTS.revenue_streams },
|
|
38
|
+
];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
import { BMC_TOOLTIPS } from "./bmc-tooltips";
|
|
42
|
+
|
|
43
|
+
export function bmcToCanvas(bmc: CompiledBmc | null, message?: string): BmcCanvasView {
|
|
44
|
+
if (!bmc) {
|
|
45
|
+
return {
|
|
46
|
+
canvasName: null,
|
|
47
|
+
nodes: [],
|
|
48
|
+
edges: [],
|
|
49
|
+
emptyBlocks: [],
|
|
50
|
+
message: message ?? 'No business model data available for this session',
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const nodes: WorkflowNode[] = [];
|
|
55
|
+
|
|
56
|
+
for (const { block, layout } of blockEntries(bmc)) {
|
|
57
|
+
const itemCount = block.items.length;
|
|
58
|
+
const badgeText = block.empty || itemCount === 0 ? 'empty' : `${itemCount} item${itemCount === 1 ? '' : 's'}`;
|
|
59
|
+
|
|
60
|
+
const items = block.items.map((item, idx) => ({
|
|
61
|
+
id: `${layout.id}-${idx + 1}`,
|
|
62
|
+
label: item.name,
|
|
63
|
+
description: item.description,
|
|
64
|
+
}));
|
|
65
|
+
|
|
66
|
+
const blockId = layout.id === 'cp' ? 'key_partnerships'
|
|
67
|
+
: layout.id === 'ka' ? 'key_activities'
|
|
68
|
+
: layout.id === 'kr' ? 'key_resources'
|
|
69
|
+
: layout.id === 'vp' ? 'value_propositions'
|
|
70
|
+
: layout.id === 'cr' ? 'customer_relationships'
|
|
71
|
+
: layout.id === 'ch' ? 'channels'
|
|
72
|
+
: layout.id === 'cs' ? 'customer_segments'
|
|
73
|
+
: layout.id === 'cost' ? 'cost_structure'
|
|
74
|
+
: layout.id === 'rev' ? 'revenue_streams'
|
|
75
|
+
: '';
|
|
76
|
+
|
|
77
|
+
const tooltipInfo = BMC_TOOLTIPS[blockId];
|
|
78
|
+
const description = tooltipInfo
|
|
79
|
+
? `${tooltipInfo.description}\n\nKey Question: "${tooltipInfo.question}"\n\nExamples: ${tooltipInfo.examples.join(', ')}`
|
|
80
|
+
: block.name;
|
|
81
|
+
|
|
82
|
+
nodes.push({
|
|
83
|
+
id: layout.id,
|
|
84
|
+
type: 'spatialContainer',
|
|
85
|
+
position: layout.position,
|
|
86
|
+
style: layout.size,
|
|
87
|
+
data: {
|
|
88
|
+
label: block.name,
|
|
89
|
+
description,
|
|
90
|
+
type: 'spatialContainer',
|
|
91
|
+
themeColor: layout.themeColor,
|
|
92
|
+
variant: layout.variant,
|
|
93
|
+
icon: layout.icon,
|
|
94
|
+
badgeText,
|
|
95
|
+
items,
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const edges: WorkflowEdge[] = [
|
|
101
|
+
{ id: 'e-ka-vp', source: 'ka', target: 'vp', type: 'animated' },
|
|
102
|
+
{ id: 'e-vp-cr', source: 'vp', target: 'cr', type: 'animated' },
|
|
103
|
+
{ id: 'e-cr-cs', source: 'cr', target: 'cs', type: 'animated' },
|
|
104
|
+
{ id: 'e-vp-ch', source: 'vp', target: 'ch', type: 'straight' },
|
|
105
|
+
{ id: 'e-ch-cs', source: 'ch', target: 'cs', type: 'animated' },
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
canvasName: bmc.canvas.name,
|
|
110
|
+
nodes,
|
|
111
|
+
edges,
|
|
112
|
+
emptyBlocks: bmc.emptyBlocks,
|
|
113
|
+
message,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export interface BmcTooltipContent {
|
|
2
|
+
title: string;
|
|
3
|
+
description: string;
|
|
4
|
+
question: string;
|
|
5
|
+
examples: string[];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const BMC_TOOLTIPS: Record<string, BmcTooltipContent> = {
|
|
9
|
+
key_partnerships: {
|
|
10
|
+
title: "Key Partnerships",
|
|
11
|
+
description: "The external companies, suppliers, or vendors that help your business operate and grow.",
|
|
12
|
+
question: "Who do we rely on outside our company to make this business work?",
|
|
13
|
+
examples: ["Cloud hosting providers", "Payment gateways", "Material suppliers", "Strategic alliances"]
|
|
14
|
+
},
|
|
15
|
+
key_activities: {
|
|
16
|
+
title: "Key Activities",
|
|
17
|
+
description: "The most critical daily tasks and operations your team must execute to deliver your offer.",
|
|
18
|
+
question: "What core things must we do every day to operate and succeed?",
|
|
19
|
+
examples: ["Software development", "AI model training", "Content creation", "Customer support"]
|
|
20
|
+
},
|
|
21
|
+
key_resources: {
|
|
22
|
+
title: "Key Resources",
|
|
23
|
+
description: "The most vital assets—physical, intellectual, human, or financial—needed to run your business.",
|
|
24
|
+
question: "What essential assets do we need to own or access to deliver our value?",
|
|
25
|
+
examples: ["Skilled engineers", "Proprietary algorithms", "Brand identity", "Capital"]
|
|
26
|
+
},
|
|
27
|
+
value_propositions: {
|
|
28
|
+
title: "Value Propositions",
|
|
29
|
+
description: "The collection of products, services, and benefits that solve customer problems or satisfy their needs.",
|
|
30
|
+
question: "Why do customers choose us over competitors? What problem are we solving?",
|
|
31
|
+
examples: ["Time savings", "Cost reduction", "Ease of use", "Premium quality"]
|
|
32
|
+
},
|
|
33
|
+
customer_relationships: {
|
|
34
|
+
title: "Customer Relationships",
|
|
35
|
+
description: "The type of interaction and ongoing relationship you establish with each customer group.",
|
|
36
|
+
question: "How do we interact with our customers across their lifecycle?",
|
|
37
|
+
examples: ["Self-service onboarding", "Dedicated account management", "Automated support"]
|
|
38
|
+
},
|
|
39
|
+
channels: {
|
|
40
|
+
title: "Channels",
|
|
41
|
+
description: "The touchpoints and pathways you use to communicate with, sell to, and deliver value to customers.",
|
|
42
|
+
question: "How do customers find, evaluate, buy, and receive our product or service?",
|
|
43
|
+
examples: ["Direct web platform", "Sales team", "Mobile app", "Partner marketplaces"]
|
|
44
|
+
},
|
|
45
|
+
customer_segments: {
|
|
46
|
+
title: "Customer Segments",
|
|
47
|
+
description: "The specific groups of people or organizations your business aims to reach and serve.",
|
|
48
|
+
question: "Who are we creating value for? Who is our ideal buyer?",
|
|
49
|
+
examples: ["Enterprise B2B teams", "Thought leaders", "Small business owners"]
|
|
50
|
+
},
|
|
51
|
+
cost_structure: {
|
|
52
|
+
title: "Cost Structure",
|
|
53
|
+
description: "All major fixed and variable expenses required to operate your business model.",
|
|
54
|
+
question: "What are the biggest costs inherent in running our business?",
|
|
55
|
+
examples: ["Server/compute costs", "Team salaries", "R&D licensing", "Marketing"]
|
|
56
|
+
},
|
|
57
|
+
revenue_streams: {
|
|
58
|
+
title: "Revenue Streams",
|
|
59
|
+
description: "The different ways your business earns cash and captures value from each customer segment.",
|
|
60
|
+
question: "How does money flow into the business? What are customers paying for?",
|
|
61
|
+
examples: ["Monthly SaaS subscriptions", "Usage-based fees", "One-time setup charges"]
|
|
62
|
+
}
|
|
63
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { WorkflowEdge, WorkflowNode } from "@/components/blocks/WorkflowCanvas";
|
|
2
|
+
|
|
3
|
+
export type BmcBlockId =
|
|
4
|
+
| 'customer_segments'
|
|
5
|
+
| 'value_propositions'
|
|
6
|
+
| 'channels'
|
|
7
|
+
| 'customer_relationships'
|
|
8
|
+
| 'revenue_streams'
|
|
9
|
+
| 'key_resources'
|
|
10
|
+
| 'key_activities'
|
|
11
|
+
| 'key_partnerships'
|
|
12
|
+
| 'cost_structure';
|
|
13
|
+
|
|
14
|
+
export interface BmcItem {
|
|
15
|
+
id: string;
|
|
16
|
+
name: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
sourceRefs: string[];
|
|
19
|
+
[k: string]: unknown;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface BmcBlock {
|
|
23
|
+
key: BmcBlockId;
|
|
24
|
+
name: string;
|
|
25
|
+
items: BmcItem[];
|
|
26
|
+
empty: boolean;
|
|
27
|
+
derived: boolean;
|
|
28
|
+
sourceRefs: string[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface CompiledBmc {
|
|
32
|
+
canvas: {
|
|
33
|
+
id: string;
|
|
34
|
+
name: string;
|
|
35
|
+
description: string;
|
|
36
|
+
derived: boolean;
|
|
37
|
+
source: { businessDocId?: string; productDocId?: string };
|
|
38
|
+
generatedAt: string;
|
|
39
|
+
};
|
|
40
|
+
customerSegments: BmcBlock;
|
|
41
|
+
valuePropositions: BmcBlock;
|
|
42
|
+
channels: BmcBlock;
|
|
43
|
+
customerRelationships: BmcBlock;
|
|
44
|
+
revenueStreams: BmcBlock;
|
|
45
|
+
keyResources: BmcBlock;
|
|
46
|
+
keyActivities: BmcBlock;
|
|
47
|
+
keyPartnerships: BmcBlock;
|
|
48
|
+
costStructure: BmcBlock & { strategicFocus?: string; drivers: string[] };
|
|
49
|
+
emptyBlocks: BmcBlockId[];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface BmcCanvasView {
|
|
53
|
+
canvasName: string | null;
|
|
54
|
+
nodes: WorkflowNode[];
|
|
55
|
+
edges: WorkflowEdge[];
|
|
56
|
+
emptyBlocks: BmcBlockId[];
|
|
57
|
+
message?: string;
|
|
58
|
+
}
|
|
@@ -81,7 +81,7 @@ function WorkflowCanvasInner({
|
|
|
81
81
|
}}
|
|
82
82
|
>
|
|
83
83
|
<Canvas
|
|
84
|
-
className="bg-background"
|
|
84
|
+
className="bg-background [&_.react-flow\_\_handle]:opacity-0 [&_.react-flow\_\_handle]:pointer-events-none [&_.react-flow\_\_node:hover_.react-flow\_\_handle]:opacity-100 [&_.react-flow\_\_node:hover_.react-flow\_\_handle]:pointer-events-auto [&_.react-flow\_\_node.selected_.react-flow\_\_handle]:opacity-100 [&_.react-flow\_\_node.selected_.react-flow\_\_handle]:pointer-events-auto [&_.react-flow\_\_handle]:transition-opacity [&_.react-flow\_\_handle]:duration-150"
|
|
85
85
|
connectionLineComponent={ConnectionLine}
|
|
86
86
|
connectionMode={ConnectionMode.Strict}
|
|
87
87
|
edges={edges}
|
|
@@ -16,6 +16,9 @@ export * from './SectionLayout'
|
|
|
16
16
|
export { WorkflowCanvas, getLayoutedElements } from './WorkflowCanvas'
|
|
17
17
|
export type { WorkflowCanvasProps, WorkflowNode, WorkflowEdge, WorkflowNodeData } from './WorkflowCanvas'
|
|
18
18
|
|
|
19
|
+
export { bmcToCanvas } from './BmcCanvas'
|
|
20
|
+
export type { BmcBlockId, BmcItem, BmcBlock, CompiledBmc, BmcCanvasView } from './BmcCanvas'
|
|
21
|
+
|
|
19
22
|
export { DashboardMetrics } from './DashboardMetrics'
|
|
20
23
|
export type { DashboardMetricsProps } from './DashboardMetrics'
|
|
21
24
|
|
|
@@ -16,7 +16,7 @@ export interface SubAgent {
|
|
|
16
16
|
subAgentName: string
|
|
17
17
|
input: string | Record<string, unknown>
|
|
18
18
|
output?: string | Record<string, unknown>
|
|
19
|
-
status: "pending" | "active" | "completed" | "error"
|
|
19
|
+
status: "pending" | "active" | "completed" | "error" | "running" | "in_progress"
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export interface AgentIndicatorProps {
|
|
@@ -12,6 +12,12 @@ import {
|
|
|
12
12
|
NodeTitle,
|
|
13
13
|
getHighlightIconColor,
|
|
14
14
|
} from "@/components/ai-elements/node";
|
|
15
|
+
import {
|
|
16
|
+
Tooltip,
|
|
17
|
+
TooltipContent,
|
|
18
|
+
TooltipProvider,
|
|
19
|
+
TooltipTrigger,
|
|
20
|
+
} from "@/components/primitives/Tooltip";
|
|
15
21
|
import { cn } from "@/lib/utils";
|
|
16
22
|
import type { ToolbarAction } from "@/components/composites/WorkflowToolbar";
|
|
17
23
|
|
|
@@ -27,6 +33,14 @@ export type SpatialContainerColorTheme =
|
|
|
27
33
|
|
|
28
34
|
export type SpatialContainerVariant = "extruded3d" | "flat" | "glassmorphism";
|
|
29
35
|
|
|
36
|
+
export type SpatialContainerItem = {
|
|
37
|
+
id: string;
|
|
38
|
+
label: string;
|
|
39
|
+
description?: string;
|
|
40
|
+
status?: "idle" | "running" | "success" | "error";
|
|
41
|
+
icon?: string;
|
|
42
|
+
};
|
|
43
|
+
|
|
30
44
|
export type SpatialContainerNodeData = {
|
|
31
45
|
label: string;
|
|
32
46
|
description?: string;
|
|
@@ -41,6 +55,8 @@ export type SpatialContainerNodeData = {
|
|
|
41
55
|
actions?: ToolbarAction[];
|
|
42
56
|
/** Badge count or sub-item summary tag */
|
|
43
57
|
badgeText?: string;
|
|
58
|
+
/** Sub-items rendered as HTML card elements inside spatial container */
|
|
59
|
+
items?: SpatialContainerItem[];
|
|
44
60
|
};
|
|
45
61
|
|
|
46
62
|
type SpatialContainerNodeProps = NodeProps & {
|
|
@@ -149,8 +165,8 @@ export const SpatialContainerNode = memo(
|
|
|
149
165
|
|
|
150
166
|
return (
|
|
151
167
|
<div className="relative group" style={{ width, height }}>
|
|
152
|
-
{/* Node Resizer */}
|
|
153
|
-
<NodeResizer minWidth={180} minHeight={140} isVisible={
|
|
168
|
+
{/* Node Resizer — disabled for clean spatial sections */}
|
|
169
|
+
<NodeResizer minWidth={180} minHeight={140} isVisible={false} />
|
|
154
170
|
|
|
155
171
|
{/* 2.5D Extrusion Side & Bottom Panels for extruded3d visual mode */}
|
|
156
172
|
{variant === "extruded3d" && (
|
|
@@ -158,14 +174,14 @@ export const SpatialContainerNode = memo(
|
|
|
158
174
|
{/* Bottom Extrusion Shadow Face */}
|
|
159
175
|
<div
|
|
160
176
|
className={cn(
|
|
161
|
-
"absolute -bottom-3 left-3 right-0 h-3 rounded-b-md transition-all duration-200",
|
|
177
|
+
"absolute -bottom-3.5 left-3.5 right-0 h-3.5 rounded-b-md transition-all duration-200 border-b border-r border-black/30 dark:border-black/60 shadow-md",
|
|
162
178
|
theme.extrusionBottom
|
|
163
179
|
)}
|
|
164
180
|
/>
|
|
165
181
|
{/* Right Side Extrusion Face */}
|
|
166
182
|
<div
|
|
167
183
|
className={cn(
|
|
168
|
-
"absolute -right-3 top-3 bottom-0 w-3 rounded-r-md transition-all duration-200",
|
|
184
|
+
"absolute -right-3.5 top-3.5 bottom-0 w-3.5 rounded-r-md transition-all duration-200 border-t border-r border-black/30 dark:border-black/60 shadow-md",
|
|
169
185
|
theme.extrusionSide
|
|
170
186
|
)}
|
|
171
187
|
/>
|
|
@@ -174,18 +190,20 @@ export const SpatialContainerNode = memo(
|
|
|
174
190
|
|
|
175
191
|
<Node
|
|
176
192
|
className={cn(
|
|
177
|
-
"relative h-full w-full flex flex-col border transition-all duration-200 ease-out overflow-hidden rounded-lg",
|
|
193
|
+
"relative h-full w-full flex flex-col border transition-all duration-200 ease-out overflow-hidden rounded-lg shadow-sm",
|
|
178
194
|
theme.border,
|
|
179
195
|
theme.bg,
|
|
180
196
|
variant === "glassmorphism" && "backdrop-blur-md bg-opacity-70 dark:bg-opacity-40",
|
|
181
|
-
variant === "extruded3d" && "shadow-
|
|
182
|
-
selected ? "border-primary border-2 shadow-primary/20 ring-2 ring-primary/30" : "shadow-sm",
|
|
197
|
+
variant === "extruded3d" && "shadow-xl border-t border-l border-white/20 dark:border-white/10",
|
|
183
198
|
isDisabled && "opacity-50"
|
|
184
199
|
)}
|
|
185
200
|
data-testid={`spatial-container-node-${id}`}
|
|
186
201
|
handles={{ target: true, source: true }}
|
|
187
202
|
status={status}
|
|
188
203
|
>
|
|
204
|
+
{/* Top 3D Highlight Line */}
|
|
205
|
+
<div className="absolute top-0 inset-x-0 h-0.5 bg-gradient-to-r from-transparent via-white/40 to-transparent pointer-events-none z-10" />
|
|
206
|
+
|
|
189
207
|
{/* Header Panel */}
|
|
190
208
|
<div
|
|
191
209
|
className={cn(
|
|
@@ -203,13 +221,42 @@ export const SpatialContainerNode = memo(
|
|
|
203
221
|
)}
|
|
204
222
|
strokeWidth={2}
|
|
205
223
|
/>
|
|
206
|
-
<div className="min-w-0 flex-1">
|
|
224
|
+
<div className="flex items-center gap-1.5 min-w-0 flex-1">
|
|
207
225
|
<NodeTitle
|
|
208
|
-
className="
|
|
226
|
+
className="truncate text-xs font-bold tracking-wider uppercase"
|
|
209
227
|
title={displayTitle}
|
|
210
228
|
>
|
|
211
229
|
{displayTitle}
|
|
212
230
|
</NodeTitle>
|
|
231
|
+
{displayDescription && (
|
|
232
|
+
<TooltipProvider>
|
|
233
|
+
<Tooltip delayDuration={100}>
|
|
234
|
+
<TooltipTrigger asChild>
|
|
235
|
+
<span className="cursor-help text-muted-foreground/60 hover:text-foreground shrink-0 transition-colors">
|
|
236
|
+
<Icon name="info" size="xs" />
|
|
237
|
+
</span>
|
|
238
|
+
</TooltipTrigger>
|
|
239
|
+
<TooltipContent
|
|
240
|
+
side="top"
|
|
241
|
+
align="start"
|
|
242
|
+
className="max-w-xs p-3 text-xs bg-popover text-popover-foreground border border-border shadow-lg space-y-2"
|
|
243
|
+
>
|
|
244
|
+
{displayDescription.split('\n\n').map((paragraph, idx) => (
|
|
245
|
+
<p
|
|
246
|
+
key={idx}
|
|
247
|
+
className={cn(
|
|
248
|
+
idx === 0 && "font-medium text-foreground",
|
|
249
|
+
idx === 1 && "italic text-primary/90 font-medium",
|
|
250
|
+
idx === 2 && "text-muted-foreground text-[11px]"
|
|
251
|
+
)}
|
|
252
|
+
>
|
|
253
|
+
{paragraph}
|
|
254
|
+
</p>
|
|
255
|
+
))}
|
|
256
|
+
</TooltipContent>
|
|
257
|
+
</Tooltip>
|
|
258
|
+
</TooltipProvider>
|
|
259
|
+
)}
|
|
213
260
|
</div>
|
|
214
261
|
</div>
|
|
215
262
|
|
|
@@ -226,19 +273,35 @@ export const SpatialContainerNode = memo(
|
|
|
226
273
|
)}
|
|
227
274
|
</div>
|
|
228
275
|
|
|
229
|
-
{/* Body Container Region for Child
|
|
230
|
-
<div className="relative flex-1 p-
|
|
231
|
-
{
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
276
|
+
{/* Body Container Region for Child HTML Card Items */}
|
|
277
|
+
<div className="relative flex-1 p-2.5 overflow-y-auto nowheel nodrag nopan custom-scrollbar pointer-events-auto flex flex-col gap-2">
|
|
278
|
+
{data.items && data.items.length > 0 ? (
|
|
279
|
+
data.items.map((item) => (
|
|
280
|
+
<div
|
|
281
|
+
key={item.id}
|
|
282
|
+
className="group relative flex items-center gap-2 rounded-md border border-border/70 bg-card/95 px-3 py-2 text-xs shadow-2xs transition-all hover:border-primary/50 hover:bg-accent/30"
|
|
283
|
+
>
|
|
284
|
+
<Icon
|
|
285
|
+
name={item.icon || "zap"}
|
|
286
|
+
size="xs"
|
|
287
|
+
className="shrink-0 text-primary"
|
|
288
|
+
strokeWidth={1.5}
|
|
289
|
+
/>
|
|
290
|
+
<div className="min-w-0 flex-1">
|
|
291
|
+
<div className="font-medium text-foreground truncate" title={item.label}>
|
|
292
|
+
{item.label}
|
|
293
|
+
</div>
|
|
294
|
+
{item.description && (
|
|
295
|
+
<div className="text-[10px] text-muted-foreground truncate" title={item.description}>
|
|
296
|
+
{item.description}
|
|
297
|
+
</div>
|
|
298
|
+
)}
|
|
299
|
+
</div>
|
|
300
|
+
</div>
|
|
301
|
+
))
|
|
302
|
+
) : (
|
|
303
|
+
<div className="absolute inset-0 bg-[radial-gradient(circle_at_center,rgba(0,0,0,0.03)_1px,transparent_1px)] dark:bg-[radial-gradient(circle_at_center,rgba(255,255,255,0.04)_1px,transparent_1px)] bg-[size:16px_16px] pointer-events-none -z-10" />
|
|
238
304
|
)}
|
|
239
|
-
|
|
240
|
-
{/* Subtle Spatial Grid Watermark / Grid Background Accent */}
|
|
241
|
-
<div className="absolute inset-0 bg-[radial-gradient(circle_at_center,rgba(0,0,0,0.03)_1px,transparent_1px)] dark:bg-[radial-gradient(circle_at_center,rgba(255,255,255,0.04)_1px,transparent_1px)] bg-[size:16px_16px] pointer-events-none -z-10" />
|
|
242
305
|
</div>
|
|
243
306
|
|
|
244
307
|
{/* Node Toolbar Actions */}
|
|
@@ -23,7 +23,7 @@ export interface SpecialistMessageData {
|
|
|
23
23
|
icon?: React.ReactNode
|
|
24
24
|
content: string
|
|
25
25
|
toolCalls?: ToolCall[]
|
|
26
|
-
status: "pending" | "active" | "completed" | "error"
|
|
26
|
+
status: "pending" | "active" | "completed" | "error" | "running" | "in_progress"
|
|
27
27
|
avatarSrc?: string
|
|
28
28
|
avatarName?: string
|
|
29
29
|
input?: string | Record<string, unknown>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { SystemMessage } from './SystemMessage'
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof SystemMessage> = {
|
|
5
|
+
title: 'Composites/SystemMessage',
|
|
6
|
+
component: SystemMessage,
|
|
7
|
+
parameters: {
|
|
8
|
+
layout: 'padded',
|
|
9
|
+
},
|
|
10
|
+
} satisfies Meta<typeof SystemMessage>
|
|
11
|
+
|
|
12
|
+
export default meta
|
|
13
|
+
type Story = StoryObj<typeof meta>
|
|
14
|
+
|
|
15
|
+
export const Default: Story = {
|
|
16
|
+
args: {
|
|
17
|
+
message: {
|
|
18
|
+
id: '1',
|
|
19
|
+
content: 'Background task completed successfully.',
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const WithLongContent: Story = {
|
|
25
|
+
args: {
|
|
26
|
+
message: {
|
|
27
|
+
id: '2',
|
|
28
|
+
content: 'The analysis has been completed and the results are now available for review. You can access the full report from the dashboard.',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const WithCustomRenderer: Story = {
|
|
34
|
+
args: {
|
|
35
|
+
message: {
|
|
36
|
+
id: '3',
|
|
37
|
+
content: 'Audio message received',
|
|
38
|
+
},
|
|
39
|
+
renderContent: (content) => (
|
|
40
|
+
<div className="flex items-center gap-2">
|
|
41
|
+
<span className="text-xs">🎵</span>
|
|
42
|
+
<span>{content}</span>
|
|
43
|
+
</div>
|
|
44
|
+
),
|
|
45
|
+
},
|
|
46
|
+
}
|