@motiadev/workbench 0.0.8 → 0.0.11

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.
@@ -1,20 +1,50 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Collapsible, CollapsibleContent } from '@/components/ui/collapsible';
3
2
  import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
4
3
  import { useLogs } from '@/stores/use-logs';
5
- import { motion } from 'framer-motion';
4
+ import { motion, AnimatePresence } from 'framer-motion';
6
5
  import { ChevronDown, ChevronUp, Trash2 } from 'lucide-react';
7
- import { useState } from 'react';
6
+ import { useState, useCallback, useRef, useEffect } from 'react';
8
7
  import { LogLevelBadge } from './log-level-badge';
9
8
  import { Button } from './ui/button';
9
+ import { cn } from '@/lib/utils';
10
+ const MIN_HEIGHT = 100;
11
+ const DEFAULT_HEIGHT = 200;
10
12
  const timestamp = (time) => {
11
13
  const date = new Date(Number(time));
12
14
  return `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`;
13
15
  };
14
16
  export const LogConsole = () => {
15
17
  const [isExpanded, setIsExpanded] = useState(false);
18
+ const [height, setHeight] = useState(DEFAULT_HEIGHT);
16
19
  const logs = useLogs((state) => state.logs);
17
20
  const resetLogs = useLogs((state) => state.resetLogs);
18
- const toggleExpand = () => setIsExpanded((prev) => !prev);
19
- return (_jsxs("div", { className: "absolute bottom-0 left-0 right-0 w-full bg-black h-fit z-40", children: [_jsxs("div", { className: "flex justify-between w-full items-center p-2", children: [_jsx("label", { className: "text-green-500 w-full text-left justify-start h-full text-lg font-bold", children: "Logs" }), logs.length > 0 && (_jsxs(Button, { variant: "link", onClick: resetLogs, className: "text-green-500", children: [_jsx(Trash2, { className: "w-4 h-4 text-green-500" }), "Clear logs"] })), _jsxs(Button, { variant: "link", onClick: toggleExpand, className: "text-green-500", children: [isExpanded && _jsx(ChevronDown, { className: "w-4 h-4 text-green-500" }), !isExpanded && _jsx(ChevronUp, { className: "w-4 h-4 text-green-500" })] })] }), isExpanded && _jsx("div", { className: "divide-solid divide-green-500 divide-y" }), _jsx(Collapsible, { open: isExpanded, className: `w-full`, children: _jsx(CollapsibleContent, { children: _jsx(motion.div, { className: "overflow-y-auto h-[25vh] flex flex-col gap-2 py-2", initial: { height: 0 }, animate: { height: '25vh' }, transition: { duration: 0.3 }, children: _jsxs(Table, { children: [_jsx(TableHeader, { className: "sticky top-0 bg-black", children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Time" }), _jsx(TableHead, { children: "Level" }), _jsx(TableHead, { children: "Trace" }), _jsx(TableHead, { children: "Flow" }), _jsx(TableHead, { children: "Message" }), _jsx(TableHead, { children: "File" })] }) }), _jsx(TableBody, { className: "text-md font-mono font-bold", children: logs.map((log, index) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "text-green-500", children: timestamp(log.time) }), _jsx(TableCell, { children: _jsx(LogLevelBadge, { level: log.level }) }), _jsx(TableCell, { children: log.traceId.split('-').pop() }), _jsx(TableCell, { children: log.flows?.join?.(', ') }), _jsx(TableCell, { children: log.msg }), _jsx(TableCell, { children: log.file })] }, index))) })] }) }) }) })] }));
21
+ const dragRef = useRef(null);
22
+ const [isDragging, setIsDragging] = useState(false);
23
+ const toggleExpand = () => setIsExpanded(!isExpanded);
24
+ const handleMouseDown = useCallback((e) => {
25
+ e.preventDefault();
26
+ setIsDragging(true);
27
+ }, []);
28
+ useEffect(() => {
29
+ const handleMouseUp = () => {
30
+ setIsDragging(false);
31
+ };
32
+ const handleMouseMove = (e) => {
33
+ if (!isDragging)
34
+ return;
35
+ const windowHeight = window.innerHeight;
36
+ const mouseY = e.clientY;
37
+ const newHeight = windowHeight - mouseY;
38
+ if (newHeight >= MIN_HEIGHT) {
39
+ setHeight(newHeight);
40
+ }
41
+ };
42
+ document.addEventListener('mousemove', handleMouseMove);
43
+ document.addEventListener('mouseup', handleMouseUp);
44
+ return () => {
45
+ document.removeEventListener('mousemove', handleMouseMove);
46
+ document.removeEventListener('mouseup', handleMouseUp);
47
+ };
48
+ }, [isDragging]);
49
+ return (_jsxs("div", { className: "absolute bottom-0 left-0 right-0 w-full bg-zinc-900/90 border-t border-zinc-700", children: [_jsx("div", { ref: dragRef, onMouseDown: handleMouseDown, className: cn('absolute -top-1 left-0 right-0 h-1 cursor-ns-resize hover:bg-green-500/20', isDragging && 'bg-green-500/40') }), _jsxs("div", { className: "flex justify-between w-full items-center p-2", children: [_jsx("label", { className: "text-green-500 w-full text-left justify-start h-full text-lg font-bold", children: "Logs" }), logs.length > 0 && (_jsxs(Button, { variant: "link", onClick: resetLogs, className: "text-green-500", children: [_jsx(Trash2, { className: "w-4 h-4 text-green-500" }), "Clear logs"] })), _jsx(Button, { variant: "link", onClick: toggleExpand, className: "text-green-500", children: isExpanded ? _jsx(ChevronDown, { className: "w-4 h-4" }) : _jsx(ChevronUp, { className: "w-4 h-4" }) })] }), _jsx(AnimatePresence, { children: isExpanded && (_jsx(motion.div, { initial: { height: 0 }, animate: { height }, exit: { height: 0 }, transition: { duration: 0.2 }, className: "overflow-hidden", children: _jsx("div", { className: "overflow-y-auto", style: { height }, children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { className: "bg-black border-b border-zinc-700 [&>th]:text-green-500 [&>th]:font-bold", children: [_jsx(TableHead, { children: "Time" }), _jsx(TableHead, { children: "Level" }), _jsx(TableHead, { children: "Trace" }), _jsx(TableHead, { children: "Flow" }), _jsx(TableHead, { children: "Message" }), _jsx(TableHead, { children: "File" })] }) }), _jsx(TableBody, { className: "text-md font-mono", children: logs.map((log, index) => (_jsxs(TableRow, { className: "border-b border-zinc-800/50", children: [_jsx(TableCell, { className: "text-green-500", children: timestamp(log.time) }), _jsx(TableCell, { children: _jsx(LogLevelBadge, { level: log.level }) }), _jsx(TableCell, { children: log.traceId.split('-').pop() }), _jsx(TableCell, { children: log.flows?.join?.(', ') }), _jsx(TableCell, { children: log.msg }), _jsx(TableCell, { children: log.file })] }, index))) })] }) }) }, "log-content")) })] }));
20
50
  };
@@ -2,48 +2,129 @@
2
2
  @tailwind components;
3
3
  @tailwind utilities;
4
4
 
5
- html, body, div, span, applet, object, iframe,
6
- h1, h2, h3, h4, h5, h6, p, blockquote, pre,
7
- a, abbr, acronym, address, big, cite, code,
8
- del, dfn, em, img, ins, kbd, q, s, samp,
9
- small, strike, strong, sub, sup, tt, var,
10
- b, u, i, center,
11
- dl, dt, dd, ol, ul, li,
12
- fieldset, form, label, legend,
13
- table, caption, tbody, tfoot, thead, tr, th, td,
14
- article, aside, canvas, details, embed,
15
- figure, figcaption, footer, header, hgroup,
16
- menu, nav, output, ruby, section, summary,
17
- time, mark, audio, video {
18
- margin: 0;
19
- padding: 0;
20
- border: 0;
21
- font: inherit;
22
- vertical-align: baseline;
5
+ html,
6
+ body,
7
+ div,
8
+ span,
9
+ applet,
10
+ object,
11
+ iframe,
12
+ h1,
13
+ h2,
14
+ h3,
15
+ h4,
16
+ h5,
17
+ h6,
18
+ p,
19
+ blockquote,
20
+ pre,
21
+ a,
22
+ abbr,
23
+ acronym,
24
+ address,
25
+ big,
26
+ cite,
27
+ code,
28
+ del,
29
+ dfn,
30
+ em,
31
+ img,
32
+ ins,
33
+ kbd,
34
+ q,
35
+ s,
36
+ samp,
37
+ small,
38
+ strike,
39
+ strong,
40
+ sub,
41
+ sup,
42
+ tt,
43
+ var,
44
+ b,
45
+ u,
46
+ i,
47
+ center,
48
+ dl,
49
+ dt,
50
+ dd,
51
+ ol,
52
+ ul,
53
+ li,
54
+ fieldset,
55
+ form,
56
+ label,
57
+ legend,
58
+ table,
59
+ caption,
60
+ tbody,
61
+ tfoot,
62
+ thead,
63
+ tr,
64
+ th,
65
+ td,
66
+ article,
67
+ aside,
68
+ canvas,
69
+ details,
70
+ embed,
71
+ figure,
72
+ figcaption,
73
+ footer,
74
+ header,
75
+ hgroup,
76
+ menu,
77
+ nav,
78
+ output,
79
+ ruby,
80
+ section,
81
+ summary,
82
+ time,
83
+ mark,
84
+ audio,
85
+ video {
86
+ margin: 0;
87
+ padding: 0;
88
+ border: 0;
89
+ font: inherit;
90
+ vertical-align: baseline;
23
91
  }
24
92
 
25
93
  /* HTML5 display-role reset for older browsers */
26
- article, aside, details, figcaption, figure,
27
- footer, header, hgroup, menu, nav, section {
28
- display: block;
94
+ article,
95
+ aside,
96
+ details,
97
+ figcaption,
98
+ figure,
99
+ footer,
100
+ header,
101
+ hgroup,
102
+ menu,
103
+ nav,
104
+ section {
105
+ display: block;
29
106
  }
30
107
  body {
31
- line-height: 1;
108
+ line-height: 1;
32
109
  }
33
- ol, ul {
34
- list-style: none;
110
+ ol,
111
+ ul {
112
+ list-style: none;
35
113
  }
36
- blockquote, q {
37
- quotes: none;
114
+ blockquote,
115
+ q {
116
+ quotes: none;
38
117
  }
39
- blockquote:before, blockquote:after,
40
- q:before, q:after {
41
- content: '';
42
- content: none;
118
+ blockquote:before,
119
+ blockquote:after,
120
+ q:before,
121
+ q:after {
122
+ content: '';
123
+ content: none;
43
124
  }
44
125
  table {
45
- border-collapse: collapse;
46
- border-spacing: 0;
126
+ border-collapse: collapse;
127
+ border-spacing: 0;
47
128
  }
48
129
 
49
130
  :root {
@@ -79,8 +160,8 @@ body {
79
160
  width: 100%;
80
161
  }
81
162
 
82
-
83
- button, textarea {
163
+ button,
164
+ textarea {
84
165
  font-family: 'PT Sans', serif;
85
166
  }
86
167
 
@@ -88,7 +169,8 @@ strong {
88
169
  font-weight: 800;
89
170
  }
90
171
 
91
- body, #root {
172
+ body,
173
+ #root {
92
174
  width: 100dvw;
93
175
  height: 100dvh;
94
176
  }
@@ -179,12 +261,23 @@ body, #root {
179
261
  }
180
262
 
181
263
  @keyframes flowDash {
182
- 0% { stroke-dashoffset: 0; }
183
- 100% { stroke-dashoffset: -20; }
264
+ 0% {
265
+ stroke-dashoffset: 0;
266
+ }
267
+ 100% {
268
+ stroke-dashoffset: -20;
269
+ }
184
270
  }
185
271
 
186
272
  .edge-animated {
187
- stroke-dasharray: 5; /* length of dash pattern */
188
- stroke-linecap: round; /* round the dash ends */
273
+ stroke-dasharray: 5; /* length of dash pattern */
274
+ stroke-linecap: round; /* round the dash ends */
189
275
  animation: flowDash 1s linear infinite;
190
- }
276
+ }
277
+
278
+ .resize-handle:hover {
279
+ background-color: rgba(74, 222, 128, 0.2);
280
+ }
281
+ .resize-handle.dragging {
282
+ background-color: rgba(74, 222, 128, 0.4);
283
+ }
@@ -1,7 +1,7 @@
1
1
  import { type VariantProps } from 'class-variance-authority';
2
2
  import { PropsWithChildren } from 'react';
3
3
  declare const baseBackgroundVariants: (props?: ({
4
- variant?: "event" | "api" | "noop" | null | undefined;
4
+ variant?: "event" | "api" | "noop" | "cron" | null | undefined;
5
5
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
6
6
  type Props = PropsWithChildren<{
7
7
  variant?: VariantProps<typeof baseBackgroundVariants>['variant'];
@@ -10,6 +10,7 @@ const baseBackgroundVariants = cva('absolute -inset-[1px] rounded-md bg-gradient
10
10
  event: 'from-teal-500/20 to-teal-400/10',
11
11
  api: 'from-blue-500/20 to-blue-400/10',
12
12
  noop: 'from-white/20 to-white/10',
13
+ cron: 'from-purple-500/20 to-purple-400/10',
13
14
  },
14
15
  },
15
16
  });
@@ -0,0 +1,2 @@
1
+ import { CronNodeProps } from './node-props';
2
+ export declare const CronNode: ({ data }: CronNodeProps) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { BaseNode } from './base-node';
3
+ import { Emits } from './emits';
4
+ import { Clock } from 'lucide-react';
5
+ export const CronNode = ({ data }) => {
6
+ return (_jsxs(BaseNode, { variant: "cron", title: data.name, headerChildren: _jsx(Clock, { className: "w-4 h-4 text-purple-400" }), disableTargetHandle: true, children: [_jsx("div", { className: "text-sm text-white/70", children: data.description }), _jsxs("div", { className: "text-xs text-white/50 flex items-center gap-2", children: [_jsx(Clock, { className: "w-3 h-3" }), " ", data.cronExpression] }), _jsx(Emits, { emits: data.emits })] }));
7
+ };
@@ -1,5 +1,5 @@
1
- import { EventNodeData, ApiNodeData, NoopNodeData } from '../views/flow/nodes/nodes.types';
2
- export type BaseNodeProps = EventNodeProps | NoopNodeProps | ApiNodeProps;
1
+ import { EventNodeData, ApiNodeData, NoopNodeData, CronNodeData } from '../views/flow/nodes/nodes.types';
2
+ export type BaseNodeProps = EventNodeProps | NoopNodeProps | ApiNodeProps | CronNodeProps;
3
3
  export type EventNodeProps = {
4
4
  data: EventNodeData;
5
5
  };
@@ -9,3 +9,6 @@ export type NoopNodeProps = {
9
9
  export type ApiNodeProps = {
10
10
  data: ApiNodeData;
11
11
  };
12
+ export type CronNodeProps = {
13
+ data: CronNodeData;
14
+ };
@@ -22,19 +22,27 @@ export const FlowView = ({ flow }) => {
22
22
  const onInitialized = useCallback(() => {
23
23
  setTimeout(() => setInitialized(true), 10);
24
24
  }, []);
25
- const highlightClass = (nodeType) => {
25
+ const getClassName = (nodeType) => {
26
26
  if (!hoveredType)
27
27
  return '';
28
- return nodeType === hoveredType
29
- ? 'shadow-[0_0_15px_rgba(255,255,255,0.15)] border border-white/30 scale-[1.02] transition-all duration-300'
30
- : 'opacity-30 transition-all duration-300';
28
+ if (nodeType) {
29
+ return nodeType === hoveredType
30
+ ? 'shadow-[0_0_15px_rgba(255,255,255,0.15)] border border-white/30 scale-[1.02] transition-all duration-300'
31
+ : 'opacity-30 transition-all duration-300';
32
+ }
33
+ // If no nodeType is provided, this is an edge
34
+ return 'opacity-30 transition-all duration-300';
31
35
  };
32
36
  const nodesWithHighlights = nodes.map((node) => ({
33
37
  ...node,
34
- className: highlightClass(node.data.type), // Access type from `data.type`
38
+ className: getClassName(node.data.type),
39
+ }));
40
+ const edgesWithHighlights = edges.map((edge) => ({
41
+ ...edge,
42
+ className: getClassName(), // No argument means it's an edge
35
43
  }));
36
44
  if (!nodeTypes) {
37
45
  return null;
38
46
  }
39
- return (_jsxs("div", { className: "w-full h-full relative bg-black", children: [!initialized && _jsx(FlowLoader, {}), _jsx(Legend, { onHover: setHoveredType }), _jsxs(ReactFlow, { nodes: nodesWithHighlights, edges: edges, nodeTypes: nodeTypes, edgeTypes: edgeTypes, onNodesChange: onNodesChange, onEdgesChange: onEdgesChange, children: [_jsx(Background, { variant: BackgroundVariant.Dots, gap: 20, size: 1, color: "#444" }), _jsx(NodeOrganizer, { onInitialized: onInitialized }), _jsx("svg", { children: _jsx("defs", { children: _jsx(ArrowHead, { color: "#B3B3B3", id: "arrowhead" }) }) })] }), _jsx(LogConsole, {})] }));
47
+ return (_jsxs("div", { className: "w-full h-full relative bg-black", children: [!initialized && _jsx(FlowLoader, {}), _jsx(Legend, { onHover: setHoveredType }), _jsxs(ReactFlow, { nodes: nodesWithHighlights, edges: edgesWithHighlights, nodeTypes: nodeTypes, edgeTypes: edgeTypes, onNodesChange: onNodesChange, onEdgesChange: onEdgesChange, children: [_jsx(Background, { variant: BackgroundVariant.Dots, gap: 20, size: 1, color: "#444" }), _jsx(NodeOrganizer, { onInitialized: onInitialized }), _jsx("svg", { children: _jsx("defs", { children: _jsx(ArrowHead, { color: "#B3B3B3", id: "arrowhead" }) }) })] }), _jsx(LogConsole, {})] }));
40
48
  };
@@ -7,7 +7,7 @@ type Emit = string | {
7
7
  type FlowStep = {
8
8
  id: string;
9
9
  name: string;
10
- type: 'event' | 'api' | 'noop';
10
+ type: 'event' | 'api' | 'noop' | 'cron';
11
11
  description?: string;
12
12
  subscribes?: string[];
13
13
  emits: Emit[];
@@ -3,11 +3,13 @@ import { useEffect, useState } from 'react';
3
3
  import { ApiFlowNode } from '../nodes/api-flow-node';
4
4
  import { NoopFlowNode } from '../nodes/noop-flow-node';
5
5
  import { EventFlowNode } from '../nodes/event-flow-node';
6
+ import { CronNode } from '@/publicComponents/cron-node';
6
7
  async function importFlow(flow) {
7
8
  const nodeTypes = {
8
9
  event: EventFlowNode,
9
10
  api: ApiFlowNode,
10
11
  noop: NoopFlowNode,
12
+ cron: CronNode,
11
13
  };
12
14
  // Load custom node components if they exist
13
15
  for (const step of flow.steps) {
@@ -1,3 +1,4 @@
1
- export declare const Legend: ({ onHover }: {
1
+ import { FC } from 'react';
2
+ export declare const Legend: FC<{
2
3
  onHover: (type: string | null) => void;
3
- }) => import("react/jsx-runtime").JSX.Element;
4
+ }>;
@@ -1,5 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { motion, AnimatePresence } from 'framer-motion';
4
+ import { ChevronDown, ChevronUp } from 'lucide-react';
5
+ import { Button } from '@/components/ui/button';
6
+ import { cn } from '@/lib/utils';
2
7
  export const Legend = ({ onHover }) => {
8
+ const [isExpanded, setIsExpanded] = useState(false);
3
9
  const legendItems = [
4
10
  {
5
11
  label: 'Event (Core)',
@@ -19,23 +25,28 @@ export const Legend = ({ onHover }) => {
19
25
  bgColor: 'bg-zinc-950/40',
20
26
  description: 'Placeholder nodes for external processes.',
21
27
  },
28
+ {
29
+ label: 'Cron',
30
+ type: 'cron',
31
+ bgColor: 'bg-purple-950/40',
32
+ description: 'Scheduled tasks that run at specified intervals.',
33
+ },
22
34
  ];
23
35
  const edgeLegendItems = [
24
36
  {
25
37
  label: 'Event Edge',
26
- color: 'rgb(133, 176, 132)', // Solid green line
38
+ color: 'rgb(133, 176, 132)',
27
39
  description: 'Represents an event emitted and subscribed by steps.',
28
40
  dashed: true,
29
41
  },
30
42
  {
31
43
  label: 'Virtual Edge',
32
- color: 'rgb(147, 169, 197)', // Dotted blue line
44
+ color: 'rgb(147, 169, 197)',
33
45
  description: 'Represents virtual connections.',
34
46
  dashed: true,
35
47
  },
36
48
  ];
37
49
  const renderSwatch = (bgColor) => (_jsxs("div", { className: "relative group", children: [_jsx("div", { className: "absolute -inset-[1px] rounded bg-gradient-to-r from-white/20 to-white/10" }), _jsx("div", { className: `relative ${bgColor} w-8 h-8 rounded border border-white/10` }), _jsx("div", { className: "absolute inset-0 -z-10 translate-y-0.5 translate-x-0.5 bg-black/20 rounded border border-white/5" })] }));
38
- const renderEdgeSwatch = (color, dashed) => (_jsx("svg", { width: "48", height: "10", viewBox: "0 0 48 10", xmlns: "http://www.w3.org/2000/svg", children: _jsx("line", { x1: "0", y1: "5", x2: "48", y2: "5", stroke: color, strokeWidth: ".8", strokeDasharray: dashed ? '4 3' : 'none', strokeLinecap: "round" // Rounded edges
39
- }) }));
40
- return (_jsxs("div", { className: "absolute right-4 top-4 font-mono rounded-lg border border-white/20 p-4 z-10 shadow-xl", children: [_jsx("div", { className: "text-sm text-white mb-3 font-semibold", children: "Flow Legend" }), _jsx("div", { className: "flex flex-col gap-3", children: legendItems.map((item) => (_jsx("div", { onMouseEnter: () => onHover(item.type), onMouseLeave: () => onHover(null), className: "group cursor-pointer transition-all hover:bg-white/5 rounded p-1 -mx-1", children: _jsxs("div", { className: "flex items-start gap-3", children: [renderSwatch(item.bgColor), _jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "text-white text-xs font-medium", children: item.label }), _jsx("div", { className: "text-white/60 text-xs mt-0.5", children: item.description })] })] }) }, item.type))) }), _jsx("div", { className: "text-sm text-white mt-4 mb-3 font-semibold", children: "Edge Legend" }), _jsx("div", { className: "flex flex-col gap-3", children: edgeLegendItems.map((item) => (_jsxs("div", { className: "flex items-start gap-3", children: [renderEdgeSwatch(item.color, item.dashed), _jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "text-white text-xs font-medium", children: item.label }), _jsx("div", { className: "text-white/60 text-xs mt-0.5", children: item.description })] })] }, item.label))) })] }));
50
+ const renderEdgeSwatch = (color, dashed) => (_jsx("svg", { width: "48", height: "10", viewBox: "0 0 48 10", xmlns: "http://www.w3.org/2000/svg", children: _jsx("line", { x1: "0", y1: "5", x2: "48", y2: "5", stroke: color, strokeWidth: ".8", strokeDasharray: dashed ? '4 3' : 'none', strokeLinecap: "round" }) }));
51
+ return (_jsx("div", { className: "absolute right-4 top-4 z-10", children: _jsxs("div", { className: cn('rounded-lg border border-zinc-700 bg-zinc-900/90', !isExpanded && 'rounded-b-lg'), children: [_jsxs("div", { className: "flex items-center justify-between p-4", children: [_jsx("div", { className: "text-sm text-white font-semibold", children: "Flow Legend" }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => setIsExpanded(!isExpanded), className: "ml-4 p-1 hover:bg-white/10", children: isExpanded ? _jsx(ChevronUp, { size: 16 }) : _jsx(ChevronDown, { size: 16 }) })] }), _jsx(AnimatePresence, { children: isExpanded && (_jsx(motion.div, { initial: { height: 0, opacity: 0 }, animate: { height: 'auto', opacity: 1 }, exit: { height: 0, opacity: 0 }, transition: { duration: 0.2 }, className: "overflow-hidden", children: _jsxs("div", { className: "p-4 pt-0", children: [_jsx("div", { className: "flex flex-col gap-3", children: legendItems.map((item) => (_jsx("div", { onMouseEnter: () => onHover(item.type), onMouseLeave: () => onHover(null), className: "group cursor-pointer transition-all hover:bg-white/5 rounded p-1 -mx-1", children: _jsxs("div", { className: "flex items-start gap-3", children: [renderSwatch(item.bgColor), _jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "text-white text-xs font-medium", children: item.label }), _jsx("div", { className: "text-white/60 text-xs mt-0.5", children: item.description })] })] }) }, item.type))) }), _jsx("div", { className: "text-sm text-white mt-4 mb-3 font-semibold", children: "Edge Legend" }), _jsx("div", { className: "flex flex-col gap-3", children: edgeLegendItems.map((item) => (_jsxs("div", { className: "flex items-start gap-3", children: [renderEdgeSwatch(item.color, item.dashed), _jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "text-white text-xs font-medium", children: item.label }), _jsx("div", { className: "text-white/60 text-xs mt-0.5", children: item.description })] })] }, item.label))) })] }) })) })] }) }));
41
52
  };
@@ -29,7 +29,18 @@ export type ApiNodeData = {
29
29
  webhookUrl?: string;
30
30
  bodySchema?: JSONSchema7;
31
31
  };
32
- export type NodeData = EventNodeData | ApiNodeData | NoopNodeData;
32
+ export type CronNodeData = {
33
+ type: string;
34
+ name: string;
35
+ description?: string;
36
+ emits: Array<string | {
37
+ type: string;
38
+ label?: string;
39
+ }>;
40
+ cronExpression: string;
41
+ language?: string;
42
+ };
43
+ export type NodeData = EventNodeData | ApiNodeData | NoopNodeData | CronNodeData;
33
44
  export type EdgeData = {
34
45
  label?: string;
35
46
  variant: 'event' | 'virtual';