ai-design-system 0.1.30 → 0.1.32
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/edge.tsx +42 -93
- package/components/ai-elements/node.tsx +1 -1
- package/components/blocks/WorkflowCanvas/WorkflowCanvas.tsx +1 -0
- package/components/composites/LoadingShimmer/LoadingShimmer.tsx +24 -0
- package/components/composites/LoadingShimmer/index.ts +2 -0
- package/components/composites/LoadingShimmer/interfaces.ts +4 -0
- package/components/composites/StateNode/StateNode.tsx +8 -5
- package/components/composites/TransitionNode/TransitionNode.tsx +7 -5
- package/components/composites/WorkflowRunObservabilityPanel/WorkflowRunObservabilityPanel.tsx +1 -1
- package/components/composites/index.ts +4 -0
- package/components/features/PageLayout/PageLayout.tsx +3 -22
- package/components/features/WorkflowObservabilityFeature/WorkflowObservabilityFeature.stories.tsx +65 -0
- package/components/features/WorkflowObservabilityFeature/WorkflowObservabilityFeature.tsx +8 -3
- package/dist/index.cjs +112 -132
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +3 -10
- package/dist/index.d.ts +6 -0
- package/dist/index.js +113 -133
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BaseEdge,
|
|
3
3
|
type EdgeProps,
|
|
4
|
-
getBezierPath,
|
|
5
4
|
getSimpleBezierPath,
|
|
6
|
-
|
|
7
|
-
type Node,
|
|
8
|
-
Position,
|
|
9
|
-
useInternalNode,
|
|
5
|
+
getSmoothStepPath,
|
|
10
6
|
} from "@xyflow/react";
|
|
11
7
|
|
|
12
8
|
const Temporary = ({
|
|
@@ -39,97 +35,49 @@ const Temporary = ({
|
|
|
39
35
|
);
|
|
40
36
|
};
|
|
41
37
|
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
case Position.Right:
|
|
63
|
-
offsetX = handle.width;
|
|
64
|
-
break;
|
|
65
|
-
case Position.Top:
|
|
66
|
-
offsetY = 0;
|
|
67
|
-
break;
|
|
68
|
-
case Position.Bottom:
|
|
69
|
-
offsetY = handle.height;
|
|
70
|
-
break;
|
|
71
|
-
default:
|
|
72
|
-
throw new Error(`Invalid handle position: ${handlePosition}`);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const x = node.internals.positionAbsolute.x + handle.x + offsetX;
|
|
76
|
-
const y = node.internals.positionAbsolute.y + handle.y + offsetY;
|
|
77
|
-
|
|
78
|
-
return [x, y] as const;
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
const getEdgeParams = (
|
|
82
|
-
source: InternalNode<Node>,
|
|
83
|
-
target: InternalNode<Node>
|
|
84
|
-
) => {
|
|
85
|
-
const sx = source.internals.positionAbsolute.x + (source.measured?.width ?? 0) / 2;
|
|
86
|
-
const sy = source.internals.positionAbsolute.y + (source.measured?.height ?? 0) / 2;
|
|
87
|
-
const tx = target.internals.positionAbsolute.x + (target.measured?.width ?? 0) / 2;
|
|
88
|
-
const ty = target.internals.positionAbsolute.y + (target.measured?.height ?? 0) / 2;
|
|
89
|
-
|
|
90
|
-
const dx = tx - sx;
|
|
91
|
-
const dy = ty - sy;
|
|
92
|
-
|
|
93
|
-
// Pick source/target positions based on dominant direction
|
|
94
|
-
let sourcePos: Position;
|
|
95
|
-
let targetPos: Position;
|
|
96
|
-
|
|
97
|
-
if (Math.abs(dx) > Math.abs(dy)) {
|
|
98
|
-
// Horizontal dominant
|
|
99
|
-
sourcePos = dx > 0 ? Position.Right : Position.Left;
|
|
100
|
-
targetPos = dx > 0 ? Position.Left : Position.Right;
|
|
101
|
-
} else {
|
|
102
|
-
// Vertical dominant
|
|
103
|
-
sourcePos = dy > 0 ? Position.Bottom : Position.Top;
|
|
104
|
-
targetPos = dy > 0 ? Position.Top : Position.Bottom;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const [srcX, srcY] = getHandleCoordsByPosition(source, sourcePos, "source");
|
|
108
|
-
const [tgtX, tgtY] = getHandleCoordsByPosition(target, targetPos, "target");
|
|
38
|
+
const Strict = ({
|
|
39
|
+
id,
|
|
40
|
+
sourceX,
|
|
41
|
+
sourceY,
|
|
42
|
+
targetX,
|
|
43
|
+
targetY,
|
|
44
|
+
sourcePosition,
|
|
45
|
+
targetPosition,
|
|
46
|
+
markerEnd,
|
|
47
|
+
style,
|
|
48
|
+
}: EdgeProps) => {
|
|
49
|
+
const [edgePath] = getSmoothStepPath({
|
|
50
|
+
sourceX,
|
|
51
|
+
sourceY,
|
|
52
|
+
sourcePosition,
|
|
53
|
+
targetX,
|
|
54
|
+
targetY,
|
|
55
|
+
targetPosition,
|
|
56
|
+
borderRadius: 0,
|
|
57
|
+
});
|
|
109
58
|
|
|
110
|
-
return {
|
|
59
|
+
return <BaseEdge id={id} markerEnd={markerEnd} path={edgePath} style={style} />;
|
|
111
60
|
};
|
|
112
61
|
|
|
113
|
-
const Animated = ({
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
targetPosition: targetPos,
|
|
62
|
+
const Animated = ({
|
|
63
|
+
id,
|
|
64
|
+
sourceX,
|
|
65
|
+
sourceY,
|
|
66
|
+
targetX,
|
|
67
|
+
targetY,
|
|
68
|
+
sourcePosition,
|
|
69
|
+
targetPosition,
|
|
70
|
+
markerEnd,
|
|
71
|
+
style,
|
|
72
|
+
}: EdgeProps) => {
|
|
73
|
+
const [edgePath] = getSmoothStepPath({
|
|
74
|
+
sourceX,
|
|
75
|
+
sourceY,
|
|
76
|
+
sourcePosition,
|
|
77
|
+
targetX,
|
|
78
|
+
targetY,
|
|
79
|
+
targetPosition,
|
|
80
|
+
borderRadius: 0,
|
|
133
81
|
});
|
|
134
82
|
|
|
135
83
|
return (
|
|
@@ -143,6 +91,7 @@ const Animated = ({ id, source, target, markerEnd, style }: EdgeProps) => {
|
|
|
143
91
|
};
|
|
144
92
|
|
|
145
93
|
export const Edge = {
|
|
94
|
+
Strict,
|
|
146
95
|
Temporary,
|
|
147
96
|
Animated,
|
|
148
97
|
};
|
|
@@ -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
|
|
26
|
+
"node-container 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
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { Shimmer } from "@/components/ai-elements/shimmer"
|
|
3
|
+
import { cn } from "@/lib/utils"
|
|
4
|
+
import type { LoadingShimmerProps } from "./interfaces"
|
|
5
|
+
|
|
6
|
+
export const LoadingShimmer = React.memo<LoadingShimmerProps>(({ message = "Loading...", className }) => {
|
|
7
|
+
return (
|
|
8
|
+
<div className={cn("flex h-full min-h-0 flex-1 items-center justify-center px-6 py-10", className)}>
|
|
9
|
+
<div className="w-full max-w-3xl space-y-5">
|
|
10
|
+
<Shimmer className="text-sm text-muted-foreground">{message}</Shimmer>
|
|
11
|
+
<div className="space-y-3">
|
|
12
|
+
<div className="h-10 w-1/3 animate-pulse rounded-md bg-muted/70" />
|
|
13
|
+
<div className="h-24 w-full animate-pulse rounded-xl bg-muted/60" />
|
|
14
|
+
<div className="grid gap-3 md:grid-cols-2">
|
|
15
|
+
<div className="h-36 animate-pulse rounded-xl bg-muted/55" />
|
|
16
|
+
<div className="h-36 animate-pulse rounded-xl bg-muted/55" />
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
LoadingShimmer.displayName = "LoadingShimmer"
|
|
@@ -68,7 +68,7 @@ export const StateNode = memo(({ data, selected, id }: StateNodeProps) => {
|
|
|
68
68
|
return (
|
|
69
69
|
<Node
|
|
70
70
|
className={cn(
|
|
71
|
-
"relative flex
|
|
71
|
+
"relative flex flex-col items-center justify-center border border-border bg-card shadow-none transition-all duration-150 ease-out",
|
|
72
72
|
selected && "border-primary border-2",
|
|
73
73
|
isTerminal && "border-2 border-primary",
|
|
74
74
|
isDisabled && "opacity-50"
|
|
@@ -87,11 +87,14 @@ export const StateNode = memo(({ data, selected, id }: StateNodeProps) => {
|
|
|
87
87
|
{/* Status indicator badge in top right */}
|
|
88
88
|
<StatusBadge status={status} />
|
|
89
89
|
|
|
90
|
-
<div className="flex items-center gap-1.5 px-3 py-2">
|
|
91
|
-
<Zap className="size-3 shrink-0 text-primary" strokeWidth={1.5} />
|
|
92
|
-
|
|
90
|
+
<div className="flex h-full w-full items-center justify-center gap-1.5 px-3 py-2">
|
|
91
|
+
<Zap className="size-3 shrink-0 text-primary" strokeWidth={1.5} />
|
|
92
|
+
<div className="min-w-0 flex-1 text-center">
|
|
93
|
+
<NodeTitle className="line-clamp-2 text-center text-xs font-medium leading-tight" title={displayTitle}>
|
|
94
|
+
{displayTitle}
|
|
95
|
+
</NodeTitle>
|
|
93
96
|
{displayDescription && (
|
|
94
|
-
<NodeDescription className="text-[10px] leading-tight
|
|
97
|
+
<NodeDescription className="mt-0.5 line-clamp-2 text-center text-[10px] leading-tight" title={displayDescription}>
|
|
95
98
|
{displayDescription}
|
|
96
99
|
</NodeDescription>
|
|
97
100
|
)}
|
|
@@ -53,7 +53,7 @@ export const TransitionNode = memo(
|
|
|
53
53
|
return (
|
|
54
54
|
<Node
|
|
55
55
|
className={cn(
|
|
56
|
-
"flex
|
|
56
|
+
"flex flex-col items-center justify-center border border-border bg-secondary text-secondary-foreground shadow-none transition-all duration-150 ease-out",
|
|
57
57
|
selected && "border-primary border-2"
|
|
58
58
|
)}
|
|
59
59
|
data-testid={`transition-node-${id}`}
|
|
@@ -78,12 +78,14 @@ export const TransitionNode = memo(
|
|
|
78
78
|
</div>
|
|
79
79
|
)}
|
|
80
80
|
|
|
81
|
-
<div className="flex items-center gap-1.5 px-3 py-2">
|
|
81
|
+
<div className="flex h-full w-full items-center justify-center gap-1.5 px-3 py-2">
|
|
82
82
|
<GitBranch className="size-3 shrink-0 text-muted-foreground" strokeWidth={1.5} />
|
|
83
|
-
<div className="
|
|
84
|
-
<NodeTitle className="text-xs font-medium leading-tight"
|
|
83
|
+
<div className="min-w-0 flex-1 text-center">
|
|
84
|
+
<NodeTitle className="line-clamp-2 text-center text-xs font-medium leading-tight" title={displayTitle}>
|
|
85
|
+
{displayTitle}
|
|
86
|
+
</NodeTitle>
|
|
85
87
|
{displayDescription && (
|
|
86
|
-
<NodeDescription className="text-[10px] leading-tight
|
|
88
|
+
<NodeDescription className="mt-0.5 line-clamp-2 text-center text-[10px] leading-tight" title={displayDescription}>
|
|
87
89
|
{displayDescription}
|
|
88
90
|
</NodeDescription>
|
|
89
91
|
)}
|
package/components/composites/WorkflowRunObservabilityPanel/WorkflowRunObservabilityPanel.tsx
CHANGED
|
@@ -484,7 +484,7 @@ export const WorkflowRunObservabilityDetailsPanel = React.memo<WorkflowRunObserv
|
|
|
484
484
|
<div className="space-y-2 border-t border-[#23242a] pt-3">
|
|
485
485
|
<div className="font-medium text-sm">Events ({events.length})</div>
|
|
486
486
|
<div className="max-h-32 divide-y divide-[#23242a] overflow-auto rounded border border-[#2a2a2f] bg-[#090a0f] [scrollbar-width:none] [-ms-overflow-style:none] [&::-webkit-scrollbar]:hidden">
|
|
487
|
-
{events.
|
|
487
|
+
{events.map((event) => (
|
|
488
488
|
<div className="px-2 py-1 text-xs" key={event.id}>
|
|
489
489
|
<div>{event.title}</div>
|
|
490
490
|
{event.timestamp ? (
|
|
@@ -118,6 +118,10 @@ export * from './AdjustableLayout'
|
|
|
118
118
|
export * from './PageContainer'
|
|
119
119
|
export type { PageContainerProps } from './PageContainer'
|
|
120
120
|
|
|
121
|
+
// LoadingShimmer Composite
|
|
122
|
+
export { LoadingShimmer } from './LoadingShimmer'
|
|
123
|
+
export type { LoadingShimmerProps } from './LoadingShimmer'
|
|
124
|
+
|
|
121
125
|
// StateNode Composite
|
|
122
126
|
export { StateNode } from './StateNode'
|
|
123
127
|
export type { StateNodeData } from './StateNode'
|
|
@@ -4,26 +4,11 @@ import { LayoutProvider } from "@/components/blocks/LayoutProvider"
|
|
|
4
4
|
import { SectionLayout } from "@/components/blocks/SectionLayout/SectionLayout"
|
|
5
5
|
import type { SectionLayoutSection } from "@/components/blocks/SectionLayout/interfaces"
|
|
6
6
|
import { AppHeader, type AppHeaderProps } from "@/components/composites/AppHeader"
|
|
7
|
+
import { LoadingShimmer } from "@/components/composites/LoadingShimmer"
|
|
7
8
|
import { PageContainer } from "@/components/composites/PageContainer"
|
|
8
9
|
|
|
9
10
|
function PageLayoutLoadingState({ message }: { message: string }) {
|
|
10
|
-
return
|
|
11
|
-
<div className="flex h-full min-h-0 flex-1 items-center justify-center px-6 py-10">
|
|
12
|
-
<div className="w-full max-w-3xl space-y-5">
|
|
13
|
-
<div className="inline-flex w-fit animate-pulse rounded-md bg-muted px-3 py-1 text-sm text-muted-foreground">
|
|
14
|
-
{message}
|
|
15
|
-
</div>
|
|
16
|
-
<div className="space-y-3">
|
|
17
|
-
<div className="h-10 w-1/3 animate-pulse rounded-md bg-muted/70" />
|
|
18
|
-
<div className="h-24 w-full animate-pulse rounded-xl bg-muted/60" />
|
|
19
|
-
<div className="grid gap-3 md:grid-cols-2">
|
|
20
|
-
<div className="h-36 animate-pulse rounded-xl bg-muted/55" />
|
|
21
|
-
<div className="h-36 animate-pulse rounded-xl bg-muted/55" />
|
|
22
|
-
</div>
|
|
23
|
-
</div>
|
|
24
|
-
</div>
|
|
25
|
-
</div>
|
|
26
|
-
)
|
|
11
|
+
return <LoadingShimmer message={message} />
|
|
27
12
|
}
|
|
28
13
|
|
|
29
14
|
/**
|
|
@@ -181,17 +166,13 @@ export const PageLayout = React.memo<PageLayoutProps>(
|
|
|
181
166
|
</PageContainer>
|
|
182
167
|
)
|
|
183
168
|
|
|
184
|
-
if (!sidebar) {
|
|
185
|
-
return pageContainer
|
|
186
|
-
}
|
|
187
|
-
|
|
188
169
|
return (
|
|
189
170
|
<LayoutProvider
|
|
190
171
|
defaultOpen={defaultSidebarOpen}
|
|
191
172
|
sidebarWidth={sidebarWidth}
|
|
192
173
|
sidebarWidthIcon={sidebarWidthIcon}
|
|
193
174
|
>
|
|
194
|
-
<AppSidebar {...sidebar} />
|
|
175
|
+
{sidebar ? <AppSidebar {...sidebar} /> : null}
|
|
195
176
|
{pageContainer}
|
|
196
177
|
</LayoutProvider>
|
|
197
178
|
)
|
package/components/features/WorkflowObservabilityFeature/WorkflowObservabilityFeature.stories.tsx
CHANGED
|
@@ -52,10 +52,75 @@ const baseArgs: Story["args"] = {
|
|
|
52
52
|
className: "h-full",
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
const hookFocusedRunActions = [
|
|
56
|
+
{ id: 'wake-up-hook', label: 'Resume Hook', resourceTypes: ['hook'], tone: 'neutral' as const, surface: 'details' as const },
|
|
57
|
+
{ id: 'cancel-hook', label: 'Cancel Hook', resourceTypes: ['hook'], tone: 'danger' as const, surface: 'menu' as const },
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
const sleepFocusedRunActions = [
|
|
61
|
+
{ id: 'wake-up-sleep', label: 'Wake Up Sleep', resourceTypes: ['sleep'], tone: 'amber' as const, surface: 'details' as const },
|
|
62
|
+
{ id: 'cancel-active-sleeps', label: 'Cancel Active Sleeps', resourceTypes: ['sleep'], tone: 'danger' as const, surface: 'menu' as const },
|
|
63
|
+
]
|
|
64
|
+
|
|
55
65
|
export const Default: Story = {
|
|
56
66
|
args: baseArgs,
|
|
57
67
|
}
|
|
58
68
|
|
|
69
|
+
export const NoSelection: Story = {
|
|
70
|
+
args: {
|
|
71
|
+
...baseArgs,
|
|
72
|
+
selectedRun: null,
|
|
73
|
+
selectedSpanId: null,
|
|
74
|
+
},
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const SelectedTraceRunDetails: Story = {
|
|
78
|
+
args: {
|
|
79
|
+
...baseArgs,
|
|
80
|
+
selectedSpanId: 'span_generateBirthdayCard',
|
|
81
|
+
},
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export const HookSuspensionState: Story = {
|
|
85
|
+
args: {
|
|
86
|
+
...baseArgs,
|
|
87
|
+
selectedSpanId: 'hook_01KP45XGJK16SW3BS6GGC5A04B',
|
|
88
|
+
runActions: hookFocusedRunActions,
|
|
89
|
+
},
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export const SleepSuspensionState: Story = {
|
|
93
|
+
args: {
|
|
94
|
+
...baseArgs,
|
|
95
|
+
selectedSpanId: 'sleep_wait_01KP45XGJK16SW3BS6GGC5A04H',
|
|
96
|
+
runActions: sleepFocusedRunActions,
|
|
97
|
+
},
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export const LiveUpdateSnapshot: Story = {
|
|
101
|
+
args: {
|
|
102
|
+
...baseArgs,
|
|
103
|
+
events: [
|
|
104
|
+
...workflowEventRecordsMock,
|
|
105
|
+
{
|
|
106
|
+
id: 'evt_4',
|
|
107
|
+
title: 'run_resumed',
|
|
108
|
+
timestamp: '4/13/2026, 12:46:12 PM',
|
|
109
|
+
description: 'Workflow resumed after manual approval.',
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
streams: [
|
|
113
|
+
...workflowStreamRecordsMock,
|
|
114
|
+
{
|
|
115
|
+
id: 'stream_3',
|
|
116
|
+
channel: 'event',
|
|
117
|
+
payload: JSON.stringify({ event_type: 'run_resumed', actor: 'manager' }),
|
|
118
|
+
timestamp: '12:46:12 PM',
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
},
|
|
122
|
+
}
|
|
123
|
+
|
|
59
124
|
export const WithStateManagement: Story = {
|
|
60
125
|
args: baseArgs,
|
|
61
126
|
render: () => {
|
|
@@ -175,6 +175,9 @@ export const WorkflowObservabilityFeature = React.memo<WorkflowObservabilityFeat
|
|
|
175
175
|
return null
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
+
const inboxDefaultSize = inbox.defaultSize ?? 25
|
|
179
|
+
const inboxMinSize = inbox.minSize ?? 25
|
|
180
|
+
|
|
178
181
|
return [
|
|
179
182
|
{
|
|
180
183
|
id: "inbox",
|
|
@@ -190,13 +193,15 @@ export const WorkflowObservabilityFeature = React.memo<WorkflowObservabilityFeat
|
|
|
190
193
|
className="h-full"
|
|
191
194
|
/>
|
|
192
195
|
),
|
|
193
|
-
|
|
196
|
+
defaultSize: inboxDefaultSize,
|
|
197
|
+
minSize: inboxMinSize,
|
|
198
|
+
maxSize: 45,
|
|
194
199
|
},
|
|
195
200
|
{
|
|
196
201
|
id: "observability",
|
|
197
202
|
content: observabilityContent,
|
|
198
|
-
defaultSize:
|
|
199
|
-
minSize:
|
|
203
|
+
defaultSize: 100 - inboxDefaultSize,
|
|
204
|
+
minSize: 50,
|
|
200
205
|
},
|
|
201
206
|
]
|
|
202
207
|
}, [inbox, observabilityContent])
|