@railtownai/railtracks-visualizer 0.0.28 → 0.0.30
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/LICENSE +21 -201
- package/README.md +2 -15
- package/dist/cjs/index.js +51 -19
- package/dist/esm/index.js +52 -20
- package/dist/types/components/AgenticFlowVisualizer.d.ts +2 -0
- package/package.json +6 -4
package/LICENSE
CHANGED
|
@@ -1,201 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
-
exercising permissions granted by this License.
|
|
25
|
-
|
|
26
|
-
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
-
including but not limited to software source code, documentation
|
|
28
|
-
source, and configuration files.
|
|
29
|
-
|
|
30
|
-
"Object" form shall mean any form resulting from mechanical
|
|
31
|
-
transformation or translation of a Source form, including but
|
|
32
|
-
not limited to compiled object code, generated documentation,
|
|
33
|
-
and conversions to other media types.
|
|
34
|
-
|
|
35
|
-
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
-
Object form, made available under the License, as indicated by a
|
|
37
|
-
copyright notice that is included in or attached to the work
|
|
38
|
-
(an example is provided in the Appendix below).
|
|
39
|
-
|
|
40
|
-
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
-
form, that is based on (or derived from) the Work and for which the
|
|
42
|
-
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
-
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
-
of this License, Derivative Works shall not include works that remain
|
|
45
|
-
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
-
the Work and Derivative Works thereof.
|
|
47
|
-
|
|
48
|
-
"Contribution" shall mean any work of authorship, including
|
|
49
|
-
the original version of the Work and any modifications or additions
|
|
50
|
-
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
-
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
-
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
-
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
-
means any form of electronic, verbal, or written communication sent
|
|
55
|
-
to the Licensor or its representatives, including but not limited to
|
|
56
|
-
communication on electronic mailing lists, source code control systems,
|
|
57
|
-
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
-
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
-
excluding communication that is conspicuously marked or otherwise
|
|
60
|
-
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
-
|
|
62
|
-
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
-
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
-
subsequently incorporated within the Work.
|
|
65
|
-
|
|
66
|
-
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
-
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
-
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
-
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
-
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
-
Work and such Derivative Works in Source or Object form.
|
|
72
|
-
|
|
73
|
-
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
-
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
-
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
-
(except as stated in this section) patent license to make, have made,
|
|
77
|
-
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
-
where such license applies only to those patent claims licensable
|
|
79
|
-
by such Contributor that are necessarily infringed by their
|
|
80
|
-
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
-
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
-
institute patent litigation against any entity (including a
|
|
83
|
-
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
-
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
-
or contributory patent infringement, then any patent licenses
|
|
86
|
-
granted to You under this License for that Work shall terminate
|
|
87
|
-
as of the date such litigation is filed.
|
|
88
|
-
|
|
89
|
-
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
-
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
-
modifications, and in Source or Object form, provided that You
|
|
92
|
-
meet the following conditions:
|
|
93
|
-
|
|
94
|
-
(a) You must give any other recipients of the Work or
|
|
95
|
-
Derivative Works a copy of this License; and
|
|
96
|
-
|
|
97
|
-
(b) You must cause any modified files to carry prominent notices
|
|
98
|
-
stating that You changed the files; and
|
|
99
|
-
|
|
100
|
-
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
-
that You distribute, all copyright, patent, trademark, and
|
|
102
|
-
attribution notices from the Source form of the Work,
|
|
103
|
-
excluding those notices that do not pertain to any part of
|
|
104
|
-
the Derivative Works; and
|
|
105
|
-
|
|
106
|
-
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
-
distribution, then any Derivative Works that You distribute must
|
|
108
|
-
include a readable copy of the attribution notices contained
|
|
109
|
-
within such NOTICE file, excluding those notices that do not
|
|
110
|
-
pertain to any part of the Derivative Works, in at least one
|
|
111
|
-
of the following places: within a NOTICE text file distributed
|
|
112
|
-
as part of the Derivative Works; within the Source form or
|
|
113
|
-
documentation, if provided along with the Derivative Works; or,
|
|
114
|
-
within a display generated by the Derivative Works, if and
|
|
115
|
-
wherever such third-party notices normally appear. The contents
|
|
116
|
-
of the NOTICE file are for informational purposes only and
|
|
117
|
-
do not modify the License. You may add Your own attribution
|
|
118
|
-
notices within Derivative Works that You distribute, alongside
|
|
119
|
-
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
-
that such additional attribution notices cannot be construed
|
|
121
|
-
as modifying the License.
|
|
122
|
-
|
|
123
|
-
You may add Your own copyright statement to Your modifications and
|
|
124
|
-
may provide additional or different license terms and conditions
|
|
125
|
-
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
-
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
-
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
-
the conditions stated in this License.
|
|
129
|
-
|
|
130
|
-
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
-
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
-
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
-
this License, without any additional terms or conditions.
|
|
134
|
-
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
-
the terms of any separate license agreement you may have executed
|
|
136
|
-
with Licensor regarding such Contributions.
|
|
137
|
-
|
|
138
|
-
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
-
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
-
except as required for reasonable and customary use in describing the
|
|
141
|
-
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
-
|
|
143
|
-
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
-
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
-
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
-
implied, including, without limitation, any warranties or conditions
|
|
148
|
-
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
-
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
-
appropriateness of using or redistributing the Work and assume any
|
|
151
|
-
risks associated with Your exercise of permissions under this License.
|
|
152
|
-
|
|
153
|
-
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
-
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
-
unless required by applicable law (such as deliberate and grossly
|
|
156
|
-
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
-
liable to You for damages, including any direct, indirect, special,
|
|
158
|
-
incidental, or consequential damages of any character arising as a
|
|
159
|
-
result of this License or out of the use or inability to use the
|
|
160
|
-
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
-
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
-
other commercial damages or losses), even if such Contributor
|
|
163
|
-
has been advised of the possibility of such damages.
|
|
164
|
-
|
|
165
|
-
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
-
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
-
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
-
or other liability obligations and/or rights consistent with this
|
|
169
|
-
License. However, in accepting such obligations, You may act only
|
|
170
|
-
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
-
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
-
defend, and hold each Contributor harmless for any liability
|
|
173
|
-
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
-
of your accepting any such warranty or additional liability.
|
|
175
|
-
|
|
176
|
-
END OF TERMS AND CONDITIONS
|
|
177
|
-
|
|
178
|
-
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
-
|
|
180
|
-
To apply the Apache License to your work, attach the following
|
|
181
|
-
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
-
replaced with your own identifying information. (Don't include
|
|
183
|
-
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
-
comment syntax for the file format. We also recommend that a
|
|
185
|
-
file or class name and description of purpose be included on the
|
|
186
|
-
same "printed page" as the copyright notice for easier
|
|
187
|
-
identification within third-party archives.
|
|
188
|
-
|
|
189
|
-
Copyright 2025 Railtown AI
|
|
190
|
-
|
|
191
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
-
you may not use this file except in compliance with the License.
|
|
193
|
-
You may obtain a copy of the License at
|
|
194
|
-
|
|
195
|
-
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
-
|
|
197
|
-
Unless required by applicable law or agreed to in writing, software
|
|
198
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
-
See the License for the specific language governing permissions and
|
|
201
|
-
limitations under the License.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Railtown AI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# RailTracks Visualizer
|
|
2
2
|
|
|
3
|
+
[](https://github.com/RailtownAI/railtracks-visualizer/actions/workflows/ci.yaml)
|
|
4
|
+
|
|
3
5
|
A React-based visualizer for RailTracks agentic flows
|
|
4
6
|
|
|
5
7
|
## 🚀 Quick Start
|
|
@@ -40,15 +42,11 @@ A React-based visualizer for RailTracks agentic flows
|
|
|
40
42
|
## 🎨 Features
|
|
41
43
|
|
|
42
44
|
- **Interactive Flow Visualization**: Visualize agentic flows with nodes and edges
|
|
43
|
-
- **Timeline Navigation**: Step through the flow execution timeline
|
|
44
45
|
- **Auto-layout**: Automatic positioning of nodes in a tree structure
|
|
45
|
-
- **Responsive Design**: Works on different screen sizes
|
|
46
46
|
- **Inspection Panel**: Detailed view of nodes and edges
|
|
47
|
-
- **Playback Controls**: Play/pause timeline progression
|
|
48
47
|
- **Portable Styling**: Uses emotion for self-contained, portable styles
|
|
49
48
|
- **Graceful Theme Handling**: Works out of the box with default light theme, no ThemeProvider required
|
|
50
49
|
- **Dark Mode Support**: Full dark mode compatibility with automatic theme detection
|
|
51
|
-
- **Icon Integration**: Support for OpenAI, Anthropic, and Google AI provider icons
|
|
52
50
|
|
|
53
51
|
### Basic Usage (No ThemeProvider Required)
|
|
54
52
|
|
|
@@ -59,17 +57,6 @@ import { Visualizer } from "@railtownai/railtracks-visualizer";
|
|
|
59
57
|
<Visualizer flowData={data} />;
|
|
60
58
|
```
|
|
61
59
|
|
|
62
|
-
### Custom Theming (Optional)
|
|
63
|
-
|
|
64
|
-
```tsx
|
|
65
|
-
import { Visualizer, ThemeProvider, darkTheme } from "@railtownai/railtracks-visualizer";
|
|
66
|
-
|
|
67
|
-
// Wrap with ThemeProvider for custom themes
|
|
68
|
-
<ThemeProvider theme={darkTheme}>
|
|
69
|
-
<Visualizer flowData={data} />
|
|
70
|
-
</ThemeProvider>;
|
|
71
|
-
```
|
|
72
|
-
|
|
73
60
|
## 🔧 Development
|
|
74
61
|
|
|
75
62
|
1. `npm install`
|
package/dist/cjs/index.js
CHANGED
|
@@ -16351,6 +16351,8 @@ const InputItemTextarea = styled(index$1)`
|
|
|
16351
16351
|
`;
|
|
16352
16352
|
|
|
16353
16353
|
const DEFAULT_COUNTUP_DURATION = 0.6;
|
|
16354
|
+
const DEFAULT_POPOVER_WIDTH = 350;
|
|
16355
|
+
const DEFAULT_POPOVER_HEIGHT = 500;
|
|
16354
16356
|
// Helper component for animating currency values
|
|
16355
16357
|
const CountUpCurrency = ({ value, prefix = "", suffix = "" })=>{
|
|
16356
16358
|
return /*#__PURE__*/ React.createElement(CountUp, {
|
|
@@ -16453,8 +16455,8 @@ const NodeDetailsPopover = ({ isVisible, onClose, nodeData, triggerRef })=>{
|
|
|
16453
16455
|
const rect = triggerRef.current.getBoundingClientRect();
|
|
16454
16456
|
const viewportWidth = window.innerWidth;
|
|
16455
16457
|
const viewportHeight = window.innerHeight;
|
|
16456
|
-
const popoverWidth =
|
|
16457
|
-
const popoverHeight =
|
|
16458
|
+
const popoverWidth = DEFAULT_POPOVER_WIDTH; // Match the width from styled component
|
|
16459
|
+
const popoverHeight = DEFAULT_POPOVER_HEIGHT; // Approximate max height
|
|
16458
16460
|
const gap = 12; // Gap between node and popover
|
|
16459
16461
|
// Check if the rect is valid (has non-zero dimensions and is in viewport)
|
|
16460
16462
|
const isValidRect = rect.width > 0 && rect.height > 0 && rect.top >= 0 && rect.left >= 0;
|
|
@@ -16702,8 +16704,8 @@ const PopoverPortal = styled.div`
|
|
|
16702
16704
|
pointer-events: none;
|
|
16703
16705
|
`;
|
|
16704
16706
|
const PopoverContent = styled.div`
|
|
16705
|
-
width:
|
|
16706
|
-
max-height:
|
|
16707
|
+
width: ${DEFAULT_POPOVER_WIDTH}px;
|
|
16708
|
+
max-height: ${DEFAULT_POPOVER_HEIGHT}px;
|
|
16707
16709
|
background-color: ${(props)=>props.theme?.colors?.background || "hsl(0 0% 100%)"};
|
|
16708
16710
|
border: 1px solid ${(props)=>props.theme?.colors?.border || "hsl(214.3 31.8% 91.4%)"};
|
|
16709
16711
|
border-radius: 0.5rem;
|
|
@@ -20149,7 +20151,6 @@ SheetDescription.displayName = Description.displayName;
|
|
|
20149
20151
|
}
|
|
20150
20152
|
// Lay out each tree, centered
|
|
20151
20153
|
// Calculate the starting position to center the entire layout in the container
|
|
20152
|
-
// Ensure we don't have negative startX which would cause rightward offset
|
|
20153
20154
|
const startX = Math.max(0, (containerWidth - totalWidth) / 2);
|
|
20154
20155
|
let xCursor = startX;
|
|
20155
20156
|
for (const root of roots){
|
|
@@ -20195,16 +20196,28 @@ const nodeTypes = {
|
|
|
20195
20196
|
Tool: Node$1,
|
|
20196
20197
|
Coordinator: Node$1
|
|
20197
20198
|
};
|
|
20198
|
-
const AgenticFlowVisualizer = ({ flowData: propFlowData, width = "
|
|
20199
|
+
const AgenticFlowVisualizer = ({ flowData: propFlowData, width = "100dvw", height = "100dvh", className = "", defaultZoom = 1, defaultPan = {
|
|
20199
20200
|
x: 0,
|
|
20200
20201
|
y: 0
|
|
20201
|
-
}, disableAutoFit = true, showTimeline = false, minNodeSpacing =
|
|
20202
|
+
}, disableAutoFit = true, defaultAutoFitDuration = 1000, defaultAutoFitDelay = 250, showTimeline = false, minNodeSpacing = 300, onInspect })=>{
|
|
20202
20203
|
const { theme, isDarkMode } = useTheme();
|
|
20203
20204
|
const themeColors = theme.colors;
|
|
20205
|
+
// Helper function to determine if we should apply minimum dimensions
|
|
20206
|
+
const shouldApplyMinDimensions = React.useCallback(()=>{
|
|
20207
|
+
const widthStr = typeof width === "string" ? width : `${width}px`;
|
|
20208
|
+
const heightStr = typeof height === "string" ? height : `${height}px`;
|
|
20209
|
+
// Don't apply minimum dimensions if using viewport units
|
|
20210
|
+
const hasViewportUnits = /\b\d*\.?\d+(vw|vh|vmin|vmax)\b/.test(widthStr) || /\b\d*\.?\d+(vw|vh|vmin|vmax)\b/.test(heightStr);
|
|
20211
|
+
return !hasViewportUnits;
|
|
20212
|
+
}, [
|
|
20213
|
+
width,
|
|
20214
|
+
height
|
|
20215
|
+
]);
|
|
20204
20216
|
// Use prop data directly, no hooks needed
|
|
20205
20217
|
const flowData = propFlowData;
|
|
20206
20218
|
// Show no data state if no flowData is available
|
|
20207
20219
|
if (!flowData) {
|
|
20220
|
+
const applyMinDimensions = shouldApplyMinDimensions();
|
|
20208
20221
|
return /*#__PURE__*/ React.createElement("div", {
|
|
20209
20222
|
style: {
|
|
20210
20223
|
width: typeof width === "number" ? `${width}px` : width,
|
|
@@ -20213,8 +20226,10 @@ const AgenticFlowVisualizer = ({ flowData: propFlowData, width = "100%", height
|
|
|
20213
20226
|
borderRadius: "8px",
|
|
20214
20227
|
background: themeColors.muted,
|
|
20215
20228
|
position: "relative",
|
|
20216
|
-
|
|
20217
|
-
|
|
20229
|
+
...applyMinDimensions && {
|
|
20230
|
+
minWidth: "800px",
|
|
20231
|
+
minHeight: "600px"
|
|
20232
|
+
},
|
|
20218
20233
|
display: "flex",
|
|
20219
20234
|
alignItems: "center",
|
|
20220
20235
|
justifyContent: "center"
|
|
@@ -20324,9 +20339,8 @@ const AgenticFlowVisualizer = ({ flowData: propFlowData, width = "100%", height
|
|
|
20324
20339
|
if (!flowData.nodes) {
|
|
20325
20340
|
return new Map();
|
|
20326
20341
|
}
|
|
20327
|
-
// Use container dimensions for centering
|
|
20328
|
-
|
|
20329
|
-
const containerWidth = Math.max(containerDimensions.width || 800, 1200);
|
|
20342
|
+
// Use actual container dimensions for proper centering
|
|
20343
|
+
const containerWidth = containerDimensions.width || 800;
|
|
20330
20344
|
return calculateAutoLayout(flowData.nodes, flowData.edges || [], {
|
|
20331
20345
|
minNodeSpacing,
|
|
20332
20346
|
containerWidth
|
|
@@ -20351,6 +20365,22 @@ const AgenticFlowVisualizer = ({ flowData: propFlowData, width = "100%", height
|
|
|
20351
20365
|
}, [
|
|
20352
20366
|
flowData.edges
|
|
20353
20367
|
]);
|
|
20368
|
+
const reactFlowInstance = useReactFlow();
|
|
20369
|
+
// Pan function that can be called by child components
|
|
20370
|
+
React.useCallback((direction, amount)=>{
|
|
20371
|
+
if (!reactFlowInstance) return;
|
|
20372
|
+
const currentViewport = reactFlowInstance.getViewport();
|
|
20373
|
+
const panAmount = direction === "right" ? amount : -amount;
|
|
20374
|
+
reactFlowInstance.setViewport({
|
|
20375
|
+
x: currentViewport.x - panAmount,
|
|
20376
|
+
y: currentViewport.y,
|
|
20377
|
+
zoom: currentViewport.zoom
|
|
20378
|
+
}, {
|
|
20379
|
+
duration: 300
|
|
20380
|
+
});
|
|
20381
|
+
}, [
|
|
20382
|
+
reactFlowInstance
|
|
20383
|
+
]);
|
|
20354
20384
|
// Convert flow data to ReactFlow format with step filtering
|
|
20355
20385
|
const nodes = React.useMemo(()=>{
|
|
20356
20386
|
const stepNodes = getNodesForStep(currentStep);
|
|
@@ -20461,7 +20491,6 @@ const AgenticFlowVisualizer = ({ flowData: propFlowData, width = "100%", height
|
|
|
20461
20491
|
y: svgP.y
|
|
20462
20492
|
};
|
|
20463
20493
|
};
|
|
20464
|
-
const reactFlowInstance = useReactFlow();
|
|
20465
20494
|
// Pan to hub node (most connected node) on initial load, unless disabled or custom settings provided
|
|
20466
20495
|
React.useEffect(()=>{
|
|
20467
20496
|
if (disableAutoFit || !reactFlowInstance || nodes.length === 0) {
|
|
@@ -20500,31 +20529,34 @@ const AgenticFlowVisualizer = ({ flowData: propFlowData, width = "100%", height
|
|
|
20500
20529
|
id: hubNodeId
|
|
20501
20530
|
}
|
|
20502
20531
|
],
|
|
20503
|
-
duration:
|
|
20532
|
+
duration: defaultAutoFitDuration,
|
|
20504
20533
|
padding: 0.2,
|
|
20505
20534
|
minZoom: defaultZoom,
|
|
20506
|
-
maxZoom:
|
|
20535
|
+
maxZoom: defaultZoom
|
|
20507
20536
|
});
|
|
20508
|
-
},
|
|
20537
|
+
}, defaultAutoFitDelay); // Small delay to ensure nodes are rendered
|
|
20509
20538
|
}
|
|
20510
20539
|
}, [
|
|
20511
20540
|
nodes,
|
|
20512
20541
|
edges,
|
|
20513
20542
|
reactFlowInstance,
|
|
20514
20543
|
disableAutoFit,
|
|
20515
|
-
defaultZoom
|
|
20544
|
+
defaultZoom,
|
|
20545
|
+
defaultAutoFitDuration,
|
|
20546
|
+
defaultAutoFitDelay
|
|
20516
20547
|
]);
|
|
20517
20548
|
return /*#__PURE__*/ React.createElement("div", {
|
|
20518
20549
|
ref: containerRef,
|
|
20519
20550
|
style: {
|
|
20520
20551
|
width: typeof width === "number" ? `${width}px` : width,
|
|
20521
20552
|
height: typeof height === "number" ? `${height}px` : height,
|
|
20553
|
+
minWidth: "800px",
|
|
20554
|
+
minHeight: "600px",
|
|
20555
|
+
boxSizing: "border-box",
|
|
20522
20556
|
border: `1px solid ${themeColors.border}`,
|
|
20523
20557
|
borderRadius: "8px",
|
|
20524
20558
|
overflow: "hidden",
|
|
20525
20559
|
position: "relative",
|
|
20526
|
-
minWidth: "800px",
|
|
20527
|
-
minHeight: "600px",
|
|
20528
20560
|
background: themeColors.background
|
|
20529
20561
|
},
|
|
20530
20562
|
className: className
|
package/dist/esm/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import React__default, { memo, useState, useCallback,
|
|
2
|
+
import React__default, { memo, useMemo, useState, useCallback, forwardRef, createContext, useContext, useRef, useLayoutEffect, useEffect, createElement } from 'react';
|
|
3
3
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
4
4
|
import * as ReactDOM from 'react-dom';
|
|
5
5
|
import ReactDOM__default, { createPortal } from 'react-dom';
|
|
@@ -16331,6 +16331,8 @@ const InputItemTextarea = styled(index$1)`
|
|
|
16331
16331
|
`;
|
|
16332
16332
|
|
|
16333
16333
|
const DEFAULT_COUNTUP_DURATION = 0.6;
|
|
16334
|
+
const DEFAULT_POPOVER_WIDTH = 350;
|
|
16335
|
+
const DEFAULT_POPOVER_HEIGHT = 500;
|
|
16334
16336
|
// Helper component for animating currency values
|
|
16335
16337
|
const CountUpCurrency = ({ value, prefix = "", suffix = "" })=>{
|
|
16336
16338
|
return /*#__PURE__*/ React__default.createElement(CountUp, {
|
|
@@ -16433,8 +16435,8 @@ const NodeDetailsPopover = ({ isVisible, onClose, nodeData, triggerRef })=>{
|
|
|
16433
16435
|
const rect = triggerRef.current.getBoundingClientRect();
|
|
16434
16436
|
const viewportWidth = window.innerWidth;
|
|
16435
16437
|
const viewportHeight = window.innerHeight;
|
|
16436
|
-
const popoverWidth =
|
|
16437
|
-
const popoverHeight =
|
|
16438
|
+
const popoverWidth = DEFAULT_POPOVER_WIDTH; // Match the width from styled component
|
|
16439
|
+
const popoverHeight = DEFAULT_POPOVER_HEIGHT; // Approximate max height
|
|
16438
16440
|
const gap = 12; // Gap between node and popover
|
|
16439
16441
|
// Check if the rect is valid (has non-zero dimensions and is in viewport)
|
|
16440
16442
|
const isValidRect = rect.width > 0 && rect.height > 0 && rect.top >= 0 && rect.left >= 0;
|
|
@@ -16682,8 +16684,8 @@ const PopoverPortal = styled.div`
|
|
|
16682
16684
|
pointer-events: none;
|
|
16683
16685
|
`;
|
|
16684
16686
|
const PopoverContent = styled.div`
|
|
16685
|
-
width:
|
|
16686
|
-
max-height:
|
|
16687
|
+
width: ${DEFAULT_POPOVER_WIDTH}px;
|
|
16688
|
+
max-height: ${DEFAULT_POPOVER_HEIGHT}px;
|
|
16687
16689
|
background-color: ${(props)=>props.theme?.colors?.background || "hsl(0 0% 100%)"};
|
|
16688
16690
|
border: 1px solid ${(props)=>props.theme?.colors?.border || "hsl(214.3 31.8% 91.4%)"};
|
|
16689
16691
|
border-radius: 0.5rem;
|
|
@@ -20129,7 +20131,6 @@ SheetDescription.displayName = Description.displayName;
|
|
|
20129
20131
|
}
|
|
20130
20132
|
// Lay out each tree, centered
|
|
20131
20133
|
// Calculate the starting position to center the entire layout in the container
|
|
20132
|
-
// Ensure we don't have negative startX which would cause rightward offset
|
|
20133
20134
|
const startX = Math.max(0, (containerWidth - totalWidth) / 2);
|
|
20134
20135
|
let xCursor = startX;
|
|
20135
20136
|
for (const root of roots){
|
|
@@ -20175,16 +20176,28 @@ const nodeTypes = {
|
|
|
20175
20176
|
Tool: Node$1,
|
|
20176
20177
|
Coordinator: Node$1
|
|
20177
20178
|
};
|
|
20178
|
-
const AgenticFlowVisualizer = ({ flowData: propFlowData, width = "
|
|
20179
|
+
const AgenticFlowVisualizer = ({ flowData: propFlowData, width = "100dvw", height = "100dvh", className = "", defaultZoom = 1, defaultPan = {
|
|
20179
20180
|
x: 0,
|
|
20180
20181
|
y: 0
|
|
20181
|
-
}, disableAutoFit = true, showTimeline = false, minNodeSpacing =
|
|
20182
|
+
}, disableAutoFit = true, defaultAutoFitDuration = 1000, defaultAutoFitDelay = 250, showTimeline = false, minNodeSpacing = 300, onInspect })=>{
|
|
20182
20183
|
const { theme, isDarkMode } = useTheme();
|
|
20183
20184
|
const themeColors = theme.colors;
|
|
20185
|
+
// Helper function to determine if we should apply minimum dimensions
|
|
20186
|
+
const shouldApplyMinDimensions = useCallback(()=>{
|
|
20187
|
+
const widthStr = typeof width === "string" ? width : `${width}px`;
|
|
20188
|
+
const heightStr = typeof height === "string" ? height : `${height}px`;
|
|
20189
|
+
// Don't apply minimum dimensions if using viewport units
|
|
20190
|
+
const hasViewportUnits = /\b\d*\.?\d+(vw|vh|vmin|vmax)\b/.test(widthStr) || /\b\d*\.?\d+(vw|vh|vmin|vmax)\b/.test(heightStr);
|
|
20191
|
+
return !hasViewportUnits;
|
|
20192
|
+
}, [
|
|
20193
|
+
width,
|
|
20194
|
+
height
|
|
20195
|
+
]);
|
|
20184
20196
|
// Use prop data directly, no hooks needed
|
|
20185
20197
|
const flowData = propFlowData;
|
|
20186
20198
|
// Show no data state if no flowData is available
|
|
20187
20199
|
if (!flowData) {
|
|
20200
|
+
const applyMinDimensions = shouldApplyMinDimensions();
|
|
20188
20201
|
return /*#__PURE__*/ React__default.createElement("div", {
|
|
20189
20202
|
style: {
|
|
20190
20203
|
width: typeof width === "number" ? `${width}px` : width,
|
|
@@ -20193,8 +20206,10 @@ const AgenticFlowVisualizer = ({ flowData: propFlowData, width = "100%", height
|
|
|
20193
20206
|
borderRadius: "8px",
|
|
20194
20207
|
background: themeColors.muted,
|
|
20195
20208
|
position: "relative",
|
|
20196
|
-
|
|
20197
|
-
|
|
20209
|
+
...applyMinDimensions && {
|
|
20210
|
+
minWidth: "800px",
|
|
20211
|
+
minHeight: "600px"
|
|
20212
|
+
},
|
|
20198
20213
|
display: "flex",
|
|
20199
20214
|
alignItems: "center",
|
|
20200
20215
|
justifyContent: "center"
|
|
@@ -20304,9 +20319,8 @@ const AgenticFlowVisualizer = ({ flowData: propFlowData, width = "100%", height
|
|
|
20304
20319
|
if (!flowData.nodes) {
|
|
20305
20320
|
return new Map();
|
|
20306
20321
|
}
|
|
20307
|
-
// Use container dimensions for centering
|
|
20308
|
-
|
|
20309
|
-
const containerWidth = Math.max(containerDimensions.width || 800, 1200);
|
|
20322
|
+
// Use actual container dimensions for proper centering
|
|
20323
|
+
const containerWidth = containerDimensions.width || 800;
|
|
20310
20324
|
return calculateAutoLayout(flowData.nodes, flowData.edges || [], {
|
|
20311
20325
|
minNodeSpacing,
|
|
20312
20326
|
containerWidth
|
|
@@ -20331,6 +20345,22 @@ const AgenticFlowVisualizer = ({ flowData: propFlowData, width = "100%", height
|
|
|
20331
20345
|
}, [
|
|
20332
20346
|
flowData.edges
|
|
20333
20347
|
]);
|
|
20348
|
+
const reactFlowInstance = useReactFlow();
|
|
20349
|
+
// Pan function that can be called by child components
|
|
20350
|
+
useCallback((direction, amount)=>{
|
|
20351
|
+
if (!reactFlowInstance) return;
|
|
20352
|
+
const currentViewport = reactFlowInstance.getViewport();
|
|
20353
|
+
const panAmount = direction === "right" ? amount : -amount;
|
|
20354
|
+
reactFlowInstance.setViewport({
|
|
20355
|
+
x: currentViewport.x - panAmount,
|
|
20356
|
+
y: currentViewport.y,
|
|
20357
|
+
zoom: currentViewport.zoom
|
|
20358
|
+
}, {
|
|
20359
|
+
duration: 300
|
|
20360
|
+
});
|
|
20361
|
+
}, [
|
|
20362
|
+
reactFlowInstance
|
|
20363
|
+
]);
|
|
20334
20364
|
// Convert flow data to ReactFlow format with step filtering
|
|
20335
20365
|
const nodes = useMemo(()=>{
|
|
20336
20366
|
const stepNodes = getNodesForStep(currentStep);
|
|
@@ -20441,7 +20471,6 @@ const AgenticFlowVisualizer = ({ flowData: propFlowData, width = "100%", height
|
|
|
20441
20471
|
y: svgP.y
|
|
20442
20472
|
};
|
|
20443
20473
|
};
|
|
20444
|
-
const reactFlowInstance = useReactFlow();
|
|
20445
20474
|
// Pan to hub node (most connected node) on initial load, unless disabled or custom settings provided
|
|
20446
20475
|
useEffect(()=>{
|
|
20447
20476
|
if (disableAutoFit || !reactFlowInstance || nodes.length === 0) {
|
|
@@ -20480,31 +20509,34 @@ const AgenticFlowVisualizer = ({ flowData: propFlowData, width = "100%", height
|
|
|
20480
20509
|
id: hubNodeId
|
|
20481
20510
|
}
|
|
20482
20511
|
],
|
|
20483
|
-
duration:
|
|
20512
|
+
duration: defaultAutoFitDuration,
|
|
20484
20513
|
padding: 0.2,
|
|
20485
20514
|
minZoom: defaultZoom,
|
|
20486
|
-
maxZoom:
|
|
20515
|
+
maxZoom: defaultZoom
|
|
20487
20516
|
});
|
|
20488
|
-
},
|
|
20517
|
+
}, defaultAutoFitDelay); // Small delay to ensure nodes are rendered
|
|
20489
20518
|
}
|
|
20490
20519
|
}, [
|
|
20491
20520
|
nodes,
|
|
20492
20521
|
edges,
|
|
20493
20522
|
reactFlowInstance,
|
|
20494
20523
|
disableAutoFit,
|
|
20495
|
-
defaultZoom
|
|
20524
|
+
defaultZoom,
|
|
20525
|
+
defaultAutoFitDuration,
|
|
20526
|
+
defaultAutoFitDelay
|
|
20496
20527
|
]);
|
|
20497
20528
|
return /*#__PURE__*/ React__default.createElement("div", {
|
|
20498
20529
|
ref: containerRef,
|
|
20499
20530
|
style: {
|
|
20500
20531
|
width: typeof width === "number" ? `${width}px` : width,
|
|
20501
20532
|
height: typeof height === "number" ? `${height}px` : height,
|
|
20533
|
+
minWidth: "800px",
|
|
20534
|
+
minHeight: "600px",
|
|
20535
|
+
boxSizing: "border-box",
|
|
20502
20536
|
border: `1px solid ${themeColors.border}`,
|
|
20503
20537
|
borderRadius: "8px",
|
|
20504
20538
|
overflow: "hidden",
|
|
20505
20539
|
position: "relative",
|
|
20506
|
-
minWidth: "800px",
|
|
20507
|
-
minHeight: "600px",
|
|
20508
20540
|
background: themeColors.background
|
|
20509
20541
|
},
|
|
20510
20542
|
className: className
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@railtownai/railtracks-visualizer",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"license": "
|
|
3
|
+
"version": "0.0.30",
|
|
4
|
+
"license": "MIT",
|
|
5
5
|
"author": "Railtown AI",
|
|
6
6
|
"description": "A visualizer for RailTracks agentic flows",
|
|
7
7
|
"main": "dist/cjs/index.js",
|
|
@@ -53,18 +53,19 @@
|
|
|
53
53
|
},
|
|
54
54
|
"scripts": {
|
|
55
55
|
"build:lib": "rollup -c",
|
|
56
|
+
"build:size": "npm pack --dry-run",
|
|
56
57
|
"build:spa": "vite build",
|
|
57
58
|
"build:types": "tsc -p tsconfig.types.json",
|
|
58
|
-
"build:size": "npm pack --dry-run",
|
|
59
59
|
"build": "npm run build:types && npm run build:lib && npm run build:spa",
|
|
60
60
|
"clean": "rm -rf dist build test-results .railtracks/ui/*",
|
|
61
61
|
"dev": "rm -rf .railtracks/ui/* && vite build && cp -r build/* .railtracks/ui",
|
|
62
62
|
"lint:fix": "prettier --write \"src/**/*.{js,jsx,ts,tsx,json,css,md}\"",
|
|
63
|
+
"lint:license": "license-checker --summary --excludePrivatePackages --failOn 'AGPL-1.0;AGPL-3.0;EPL-1.0;EPL-2.0;GPL-1.0;GPL-2.0;GPL-3.0;LGPL-2.0;LGPL-2.1;LGPL-3.0;'",
|
|
63
64
|
"lint": "prettier --check \"src/**/*.{js,jsx,ts,tsx,json,css,md}\"",
|
|
64
65
|
"start": "storybook dev -p 6006",
|
|
65
66
|
"storybook:build": "cross-env NODE_ENV=production storybook build",
|
|
66
|
-
"storybook:preview": "npx http-server ./storybook-static",
|
|
67
67
|
"storybook:deploy": "gh-pages -d storybook-static",
|
|
68
|
+
"storybook:preview": "npx http-server ./storybook-static",
|
|
68
69
|
"test:ui": "vitest --ui",
|
|
69
70
|
"test": "vitest run --silent",
|
|
70
71
|
"up": "ncu -u -x react -x react-dom -x @types/react -x @types/react-dom && npm install"
|
|
@@ -95,6 +96,7 @@
|
|
|
95
96
|
"cross-env": "^10.0.0",
|
|
96
97
|
"gh-pages": "6.3.0",
|
|
97
98
|
"jsdom": "26.1.0",
|
|
99
|
+
"license-checker": "25.0.1",
|
|
98
100
|
"npm-check-updates": "18.1.0",
|
|
99
101
|
"playwright": "^1.55.0",
|
|
100
102
|
"prettier": "3.6.2",
|