- {/* Sidebar */}
-
+
+
- {/* Main Graph Area */}
-
-
-
-
-
-
-
- {menu && }
- {copyFeedback && (
-
- {copyFeedback}
-
- )}
-
-
-
-
-
-
-
-
-
- {showKeyboardShortcuts && (
-
-
- Keyboard Shortcuts:
-
-
- Ctrl+C: Copy selected node
- Ctrl+V: Paste copied node
- Ctrl+D: Duplicate selected node
- Del/Backspace: Delete selection
- Right-click: Context menu
-
- )}
-
-
-
+
+ {/* Log Dock */}
+
setDockOpen(false)}
+ lines={logLines}
+ progress={null}
+ />
+ {/* Node Sidebar */}
{
isDocumentationExpanded={isDocumentationExpanded}
setIsDocumentationExpanded={setIsDocumentationExpanded}
/>
- {selectedEdge && (
-
-
-
Selected Edge
-
- ID: {selectedEdge.id}
-
-
- Source: {selectedEdge.source}
-
-
- Target: {selectedEdge.target}
-
-
- Type: {selectedEdge.type}
-
-
-
-
-
-
-
- )}
+ {/* Edge Details */}
+ setSelectedEdge(null)}
+ onDelete={deleteSelectedEdge}
+ />
)}
@@ -1453,369 +1023,11 @@ const DnDFlow = () => {
{/* Solver Parameters Tab */}
{activeTab === 'solver' && (
-
-
-
- Solver Parameters
-
-
-
-
-
- setSolverParams({ ...solverParams, dt: e.target.value })}
- style={{
- width: '95%',
- padding: '10px',
- borderRadius: '5px',
- border: '1px solid #555',
- backgroundColor: '#1e1e2f',
- color: '#ffffff',
- fontSize: '14px'
- }}
- />
-
-
-
-
- setSolverParams({ ...solverParams, dt_min: e.target.value })}
- style={{
- width: '95%',
- padding: '10px',
- borderRadius: '5px',
- border: '1px solid #555',
- backgroundColor: '#1e1e2f',
- color: '#ffffff',
- fontSize: '14px'
- }}
- />
-
-
-
-
- setSolverParams({ ...solverParams, dt_max: e.target.value })}
- style={{
- width: '95%',
- padding: '10px',
- borderRadius: '5px',
- border: '1px solid #555',
- backgroundColor: '#1e1e2f',
- color: '#ffffff',
- fontSize: '14px'
- }}
- />
-
-
-
-
-
-
-
-
-
- setSolverParams({ ...solverParams, tolerance_fpi: e.target.value })}
- style={{
- width: '95%',
- padding: '10px',
- borderRadius: '5px',
- border: '1px solid #555',
- backgroundColor: '#1e1e2f',
- color: '#ffffff',
- fontSize: '14px'
- }}
- />
-
-
-
-
- setSolverParams({ ...solverParams, iterations_max: e.target.value })}
- style={{
- width: '95%',
- padding: '10px',
- borderRadius: '5px',
- border: '1px solid #555',
- backgroundColor: '#1e1e2f',
- color: '#ffffff',
- fontSize: '14px'
- }}
- />
-
-
-
-
- setSolverParams({ ...solverParams, simulation_duration: e.target.value })}
- style={{
- width: '95%',
- padding: '10px',
- borderRadius: '5px',
- border: '1px solid #555',
- backgroundColor: '#1e1e2f',
- color: '#ffffff',
- fontSize: '14px'
- }}
- />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Parameter Descriptions:
-
- - dt: Base time step for simulation
- - dt_min: Minimum allowed time step
- - dt_max: Maximum allowed time step
- - Solver: Numerical integration method
- - tolerance_fpi: Tolerance for fixed point iterations
- - iterations_max: Maximum number of iterations per time step
- - simulation_duration: Total duration of the simulation (in time units)
- - log: Enable/disable logging during simulation
- - extra_params: Additional solver parameters as JSON dictionary (e.g., tolerance_lte_abs, tolerance_lte_rel for numerical solvers)
-
-
-
-
+
)}
{/* Global Variables Tab */}
@@ -1831,76 +1043,14 @@ const DnDFlow = () => {
{/* Results Tab */}
{activeTab === 'results' && (
-
-
- {simulationResults ? (
- <>
-
-
-
-
-
-
- >
- ) : (
-
- No simulation results yet. Run a simulation from the Graph Editor tab to see results here.
-
- )}
-
-
+
)}
-
{ setDockOpen(false); if (sseRef.current) sseRef.current.close(); }}
- lines={logLines}
- progress={null}
- />
+
);
}
diff --git a/src/components/EdgeDetails.jsx b/src/components/EdgeDetails.jsx
new file mode 100644
index 00000000..800341f5
--- /dev/null
+++ b/src/components/EdgeDetails.jsx
@@ -0,0 +1,73 @@
+import React from 'react';
+
+export default function EdgeDetails({ edge, onClose, onDelete }) {
+ if (!edge) return null;
+
+ return (
+
+
+
Selected Edge
+
+
+ ID: {edge.id}
+
+
+ Source: {edge.source}
+
+
+ Target: {edge.target}
+
+
+ Type: {edge.type}
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/GraphView.jsx b/src/components/GraphView.jsx
new file mode 100644
index 00000000..652b9b30
--- /dev/null
+++ b/src/components/GraphView.jsx
@@ -0,0 +1,267 @@
+import { ReactFlow, Controls, MiniMap, Background } from '@xyflow/react';
+import ContextMenu from './ContextMenu';
+
+export default function GraphView(props) {
+ const {
+ refEl, nodes, edges, onNodesChange, onEdgesChange, onConnect,
+ onNodeClick, onEdgeClick, onPaneClick, onNodeContextMenu,
+ nodeTypes, onDrop, onDragStart, onDragOver,
+ menu, duplicateNode, copyFeedback,
+ ui, reactFlowWrapperRef,
+ } = props;
+
+ return (
+
+
+
+
+
+
+
+ {menu && }
+
+
+
+ {ui.showKeyboardShortcuts && ui.setShowKeyboardShortcuts(false)} />}
+
+
+
+ );
+}
+
+function FloatingButtons({
+ selectedNode, selectedEdge,
+ deleteSelectedNode, deleteSelectedEdge,
+ saveGraph, loadGraph, resetGraph, saveToPython, runPathsim,
+ dockOpen, onToggleLogs
+}) {
+ return (
+ <>
+ {/* Delete Edge, Delete Node, Save, Load, New graph, Save to Python, Run */}
+
+
+
+
+
+
+
+
+ >
+ );
+}
+
+function CopyToast({ copyFeedback }) {
+ if (!copyFeedback) return null;
+ return (
+
+ {copyFeedback}
+
+ );
+}
+
+function KeyboardShortcuts({ show, onClose }) {
+ if (!show) return null; // nothing rendered if false
+
+ return (
+
+
+ Keyboard Shortcuts:
+
+
+ Ctrl+C: Copy selected node
+ Ctrl+V: Paste copied node
+ Ctrl+D: Duplicate selected node
+ Del/Backspace: Delete selection
+ Right-click: Context menu
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/ResultsPanel.jsx b/src/components/ResultsPanel.jsx
new file mode 100644
index 00000000..947e7b24
--- /dev/null
+++ b/src/components/ResultsPanel.jsx
@@ -0,0 +1,67 @@
+import React from 'react';
+import Plot from 'react-plotly.js';
+
+export default function ResultsPanel({ simulationResults, downloadHtml, downloadCsv }) {
+ return (
+
+
+ {simulationResults ? (
+ <>
+
+
+
+
+
+
+ >
+ ) : (
+
+ No simulation results yet. Run a simulation from the Graph Editor tab to see results here.
+
+ )}
+
+
+ );
+}
diff --git a/src/components/SolverPanel.jsx b/src/components/SolverPanel.jsx
new file mode 100644
index 00000000..84e78f18
--- /dev/null
+++ b/src/components/SolverPanel.jsx
@@ -0,0 +1,369 @@
+import React from 'react';
+
+export default function SolverPanel({ solverParams, setSolverParams, onBack }) {
+ return (
+
+
+
+ Solver Parameters
+
+
+
+
+
+ setSolverParams({ ...solverParams, dt: e.target.value })}
+ style={{
+ width: '95%',
+ padding: '10px',
+ borderRadius: '5px',
+ border: '1px solid #555',
+ backgroundColor: '#1e1e2f',
+ color: '#ffffff',
+ fontSize: '14px'
+ }}
+ />
+
+
+
+
+ setSolverParams({ ...solverParams, dt_min: e.target.value })}
+ style={{
+ width: '95%',
+ padding: '10px',
+ borderRadius: '5px',
+ border: '1px solid #555',
+ backgroundColor: '#1e1e2f',
+ color: '#ffffff',
+ fontSize: '14px'
+ }}
+ />
+
+
+
+
+ setSolverParams({ ...solverParams, dt_max: e.target.value })}
+ style={{
+ width: '95%',
+ padding: '10px',
+ borderRadius: '5px',
+ border: '1px solid #555',
+ backgroundColor: '#1e1e2f',
+ color: '#ffffff',
+ fontSize: '14px'
+ }}
+ />
+
+
+
+
+
+
+
+
+
+ setSolverParams({ ...solverParams, tolerance_fpi: e.target.value })}
+ style={{
+ width: '95%',
+ padding: '10px',
+ borderRadius: '5px',
+ border: '1px solid #555',
+ backgroundColor: '#1e1e2f',
+ color: '#ffffff',
+ fontSize: '14px'
+ }}
+ />
+
+
+
+
+ setSolverParams({ ...solverParams, iterations_max: e.target.value })}
+ style={{
+ width: '95%',
+ padding: '10px',
+ borderRadius: '5px',
+ border: '1px solid #555',
+ backgroundColor: '#1e1e2f',
+ color: '#ffffff',
+ fontSize: '14px'
+ }}
+ />
+
+
+
+
+ setSolverParams({ ...solverParams, simulation_duration: e.target.value })}
+ style={{
+ width: '95%',
+ padding: '10px',
+ borderRadius: '5px',
+ border: '1px solid #555',
+ backgroundColor: '#1e1e2f',
+ color: '#ffffff',
+ fontSize: '14px'
+ }}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Parameter Descriptions:
+
+ - dt: Base time step for simulation
+ - dt_min: Minimum allowed time step
+ - dt_max: Maximum allowed time step
+ - Solver: Numerical integration method
+ - tolerance_fpi: Tolerance for fixed point iterations
+ - iterations_max: Maximum number of iterations per time step
+ - simulation_duration: Total duration of the simulation (in time units)
+ - log: Enable/disable logging during simulation
+ - extra_params: Additional solver parameters as JSON dictionary (e.g., tolerance_lte_abs, tolerance_lte_rel for numerical solvers)
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/components/TopBar.jsx b/src/components/TopBar.jsx
new file mode 100644
index 00000000..08e335aa
--- /dev/null
+++ b/src/components/TopBar.jsx
@@ -0,0 +1,125 @@
+import '../styles/App.css';
+
+const TABS = [
+ { id: 'graph', label: 'Graph Editor' },
+ { id: 'events', label: 'Events' },
+ { id: 'solver', label: 'Solver Parameters' },
+ { id: 'globals', label: 'Global Variables' },
+ { id: 'results', label: 'Results' },
+];
+
+export default function TopBar({ activeTab, setActiveTab, versionInfo }) {
+ return (
+
+
+
PathView
+ {TABS.map(t => (
+
+
+ ))}
+
+
+
+
+ );
+}
+
+function HeaderActions({ versionInfo }) {
+ return (
+
+ {/* GitHubLink */}
+
{
+ e.target.style.backgroundColor = '#1b1f23';
+ e.target.style.transform = 'translateY(-1px)';
+ e.target.style.boxShadow = '0 4px 12px rgba(36, 41, 46, 0.4)';
+ }}
+ onMouseLeave={(e) => {
+ e.target.style.backgroundColor = '#000000ff';
+ e.target.style.transform = 'translateY(0)';
+ e.target.style.boxShadow = '0 2px 6px rgba(36, 41, 46, 0.3)';
+ }}
+ title="View on GitHub"
+ >
+
+
+ {/* HelpButton */}
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/styles/App.css b/src/styles/App.css
index de3d8296..0ea9b2f0 100644
--- a/src/styles/App.css
+++ b/src/styles/App.css
@@ -1,62 +1,54 @@
-/* It seems like these styles were here by default, I commented them out to not interfere with the current design. */
-/* #root {
- max-width: 1280px;
- margin: 0 auto;
- padding: 2rem;
- text-align: center;
-}
-
-.logo {
- height: 6em;
- padding: 1.5em;
- will-change: filter;
- transition: filter 300ms;
-}
-.logo:hover {
- filter: drop-shadow(0 0 2em #646cffaa);
-}
-.logo.react:hover {
- filter: drop-shadow(0 0 2em #61dafbaa);
-} */
-
-/* This is the code for customizing the controls icons */
+/* For customizing the controls icons */
.react-flow__controls {
- padding: 6px;
- display: flex;
+ padding: 6px;
+ display: flex;
}
.react-flow__controls-button {
background-color: #007bff !important;
- color: white !important;
+ color: white !important;
border: none;
}
.react-flow__controls-button:hover {
background-color: #0056b3 !important;
}
-/*
-@keyframes logo-spin {
- from {
- transform: rotate(0deg);
- }
- to {
- transform: rotate(360deg);
- }
+
+/* App container */
+.app {
+ width: 100vw;
+ height: 100vh;
+ display: flex;
+ flex-direction: column;
}
-@media (prefers-reduced-motion: no-preference) {
- a:nth-of-type(2) .logo {
- animation: logo-spin infinite 20s linear;
- }
+/* Top bar */
+.top-bar {
+ height: 50px;
+ background: #2c2c2c;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ z-index: 15;
+ border-bottom: 1px solid #ccc;
+}
+
+/* Tab button base */
+.tab-btn {
+ padding: 10px 20px;
+ margin: 5px;
+ background-color: #444;
+ color: #fff;
+ border: none;
+ border-radius: 5px;
+ cursor: pointer;
}
-.card {
- padding: 2em;
+/* Tab button active state */
+.tab-btn.active {
+ background-color: #78A083;
}
-.read-the-docs {
- color: #888;
-} */
/* Context menu styles */
.context-menu {
@@ -101,11 +93,11 @@
line-height: 1.4;
}
-.documentation-content h1,
-.documentation-content h2,
-.documentation-content h3,
-.documentation-content h4,
-.documentation-content h5,
+.documentation-content h1,
+.documentation-content h2,
+.documentation-content h3,
+.documentation-content h4,
+.documentation-content h5,
.documentation-content h6 {
color: #ffffff;
margin: 1em 0 0.5em 0;
@@ -148,7 +140,7 @@
color: #e8e8e8;
}
-.documentation-content ul,
+.documentation-content ul,
.documentation-content ol {
margin: 0.5em 0;
padding-left: 1.5em;
@@ -172,7 +164,7 @@
margin: 0.5em 0;
}
-.documentation-content th,
+.documentation-content th,
.documentation-content td {
border: 1px solid #555;
padding: 0.4em;
@@ -280,6 +272,7 @@
.sidebar-scrollable {
scroll-behavior: smooth;
}
+
/* Sidebar and drag-and-drop styles */
.dndflow {
display: flex;
@@ -438,4 +431,4 @@ aside::-webkit-scrollbar-thumb {
aside::-webkit-scrollbar-thumb:hover {
background: #666;
-}
+}
\ No newline at end of file