Add experiment logs to the monitoring page #48
16
package-lock.json
generated
16
package-lock.json
generated
@@ -10,6 +10,7 @@
|
||||
"dependencies": {
|
||||
"@neodrag/react": "^2.3.1",
|
||||
"@xyflow/react": "^12.8.6",
|
||||
"clsx": "^2.1.1",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"react-router": "^7.9.3",
|
||||
@@ -3971,6 +3972,15 @@
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/clsx": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/co": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
||||
@@ -6945,9 +6955,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-router": {
|
||||
"version": "7.9.3",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.3.tgz",
|
||||
"integrity": "sha512-4o2iWCFIwhI/eYAIL43+cjORXYn/aRQPgtFRRZb3VzoyQ5Uej0Bmqj7437L97N9NJW4wnicSwLOLS+yCXfAPgg==",
|
||||
"version": "7.12.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.12.0.tgz",
|
||||
"integrity": "sha512-kTPDYPFzDVGIIGNLS5VJykK0HfHLY5MF3b+xj0/tTyNYL1gF1qs7u67Z9jEhQk2sQ98SUaHxlG31g1JtF7IfVw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cookie": "^1.0.1",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"dependencies": {
|
||||
"@neodrag/react": "^2.3.1",
|
||||
"@xyflow/react": "^12.8.6",
|
||||
"clsx": "^2.1.1",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"react-router": "^7.9.3",
|
||||
|
||||
@@ -12,6 +12,7 @@ import {useShallow} from 'zustand/react/shallow';
|
||||
import orderPhaseNodeArray from "../../utils/orderPhaseNodes.ts";
|
||||
import useProgramStore from "../../utils/programStore.ts";
|
||||
import {DndToolbar} from './visualProgrammingUI/components/DragDropSidebar.tsx';
|
||||
import {WarningsSidebar} from "./visualProgrammingUI/components/WarningSidebar.tsx";
|
||||
import type {PhaseNode} from "./visualProgrammingUI/nodes/PhaseNode.tsx";
|
||||
import useFlowStore from './visualProgrammingUI/VisProgStores.tsx';
|
||||
import type {FlowState} from './visualProgrammingUI/VisProgTypes.tsx';
|
||||
@@ -93,7 +94,7 @@ const VisProgUI = () => {
|
||||
|
||||
|
||||
return (
|
||||
<div className={`${styles.innerEditorContainer} round-lg border-lg`} style={({'--flow-zoom': zoom} as CSSProperties)}>
|
||||
<div className={`${styles.innerEditorContainer} round-lg border-lg flex-row`} style={({'--flow-zoom': zoom} as CSSProperties)}>
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
@@ -113,6 +114,7 @@ const VisProgUI = () => {
|
||||
snapToGrid
|
||||
fitView
|
||||
proOptions={{hideAttribution: true}}
|
||||
style={{flexGrow: 3}}
|
||||
>
|
||||
<Panel position="top-center" className={styles.dndPanel}>
|
||||
<DndToolbar/> {/* contains the drag and drop panel for nodes */}
|
||||
@@ -128,6 +130,7 @@ const VisProgUI = () => {
|
||||
<Controls/>
|
||||
<Background/>
|
||||
</ReactFlow>
|
||||
<WarningsSidebar/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -190,10 +193,8 @@ function graphReducer() {
|
||||
*/
|
||||
function VisProgPage() {
|
||||
const [programValidity, setProgramValidity] = useState<boolean>(true);
|
||||
|
||||
const {isProgramValid, severityIndex} = useFlowStore();
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
setProgramValidity(isProgramValid);
|
||||
// the following eslint disable is required as it wants us to use all possible dependencies for the useEffect statement,
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
type XYPosition,
|
||||
} from '@xyflow/react';
|
||||
import '@xyflow/react/dist/style.css';
|
||||
import { editorWarningRegistry } from "./EditorWarnings.tsx";
|
||||
import { editorWarningRegistry } from "./components/EditorWarnings.tsx";
|
||||
import type { FlowState } from './VisProgTypes';
|
||||
import {
|
||||
NodeDefaults,
|
||||
|
||||
@@ -9,7 +9,7 @@ import type {
|
||||
OnEdgesDelete,
|
||||
OnNodesDelete
|
||||
} from '@xyflow/react';
|
||||
import type {EditorWarningRegistry} from "./EditorWarnings.tsx";
|
||||
import type {EditorWarningRegistry} from "./components/EditorWarnings.tsx";
|
||||
import type {HandleRule} from "./HandleRuleLogic.ts";
|
||||
import type { NodeTypes } from './NodeRegistry';
|
||||
import type {FlowSnapshot} from "./EditorUndoRedo.ts";
|
||||
|
||||
@@ -5,12 +5,11 @@
|
||||
* - if there is no completely connected chain of startNode-[PhaseNodes]-EndNode
|
||||
* then hide any startNode, phaseNode, or endNode specific warnings
|
||||
*/
|
||||
import useFlowStore from "../VisProgStores.tsx";
|
||||
import type {FlowState} from "../VisProgTypes.tsx";
|
||||
|
||||
// --| Type definitions |--
|
||||
|
||||
import type {FlowState} from "./VisProgTypes.tsx";
|
||||
|
||||
|
||||
export type WarningId = NodeId | "GLOBAL_WARNINGS";
|
||||
export type NodeId = string;
|
||||
|
||||
@@ -202,3 +201,16 @@ export function editorWarningRegistry(get: ZustandGet, set: ZustandSet) : Editor
|
||||
},
|
||||
}}
|
||||
|
||||
|
||||
// returns a summary of the warningRegistry
|
||||
export function warningSummary() {
|
||||
const {severityIndex, isProgramValid} = useFlowStore.getState();
|
||||
return {
|
||||
info: severityIndex.get('INFO')!.size,
|
||||
warning: severityIndex.get('WARNING')!.size,
|
||||
error: severityIndex.get('ERROR')!.size,
|
||||
isValid: isProgramValid(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
.warnings-sidebar {
|
||||
width: 320px;
|
||||
height: 100%;
|
||||
background: canvas;
|
||||
border-left: 2px solid black;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.warnings-header {
|
||||
padding: 12px;
|
||||
border-bottom: 1px solid #2a2a2e;
|
||||
}
|
||||
|
||||
.severity-tabs {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.severity-tab {
|
||||
flex: 1;
|
||||
padding: 4px;
|
||||
background: ButtonFace;
|
||||
color: GrayText;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.count {
|
||||
padding: 4px;
|
||||
color: GrayText;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.severity-tab.active {
|
||||
color: ButtonText;
|
||||
border: 2px solid currentColor;
|
||||
}
|
||||
|
||||
.warnings-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.warnings-empty {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.warning-item {
|
||||
display: flex;
|
||||
margin: 5px;
|
||||
gap: 8px;
|
||||
padding: 8px 12px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.warning-item--error {
|
||||
border: 2px solid red;
|
||||
}
|
||||
|
||||
.warning-item--warning {
|
||||
border-left: 3px solid orange;
|
||||
}
|
||||
|
||||
.warning-item--info {
|
||||
border-left: 3px solid steelblue;
|
||||
}
|
||||
|
||||
.warning-item .meta {
|
||||
font-size: 11px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
import clsx from "clsx";
|
||||
import {useEffect, useState} from "react";
|
||||
import useFlowStore from "../VisProgStores.tsx";
|
||||
import {
|
||||
warningSummary,
|
||||
type WarningSeverity, type EditorWarning
|
||||
} from "./EditorWarnings.tsx";
|
||||
import styles from "./WarningSidebar.module.css";
|
||||
|
||||
export function WarningsSidebar() {
|
||||
const warnings = useFlowStore.getState().getWarnings();
|
||||
|
||||
const [severityFilter, setSeverityFilter] = useState<WarningSeverity | 'ALL'>('ALL');
|
||||
|
||||
useEffect(() => {}, [warnings]);
|
||||
const filtered = severityFilter === 'ALL'
|
||||
? warnings
|
||||
: warnings.filter(w => w.severity === severityFilter);
|
||||
|
||||
return (
|
||||
<aside className={styles.warningsSidebar}>
|
||||
<WarningsHeader
|
||||
severityFilter={severityFilter}
|
||||
onChange={setSeverityFilter}
|
||||
/>
|
||||
|
||||
<WarningsList warnings={filtered} />
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
|
||||
function WarningsHeader({
|
||||
severityFilter,
|
||||
onChange,
|
||||
}: {
|
||||
severityFilter: WarningSeverity | 'ALL';
|
||||
onChange: (severity: WarningSeverity | 'ALL') => void;
|
||||
}) {
|
||||
const summary = warningSummary();
|
||||
|
||||
return (
|
||||
<div className={styles.warningsHeader}>
|
||||
<h3>Warnings</h3>
|
||||
|
||||
<div className={styles.severityTabs}>
|
||||
{(['ALL', 'ERROR', 'WARNING', 'INFO'] as const).map(severity => (
|
||||
<button
|
||||
key={severity}
|
||||
className={clsx(styles.severityTab, severityFilter === severity && styles.active)}
|
||||
onClick={() => onChange(severity)}
|
||||
>
|
||||
{severity}
|
||||
{severity !== 'ALL' && (
|
||||
<span className={styles.count}>
|
||||
{summary[severity.toLowerCase() as keyof typeof summary]}
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function WarningsList(props: { warnings: EditorWarning[] }) {
|
||||
if (props.warnings.length === 0) {
|
||||
return (
|
||||
<div className={styles.warningsEmpty}>
|
||||
No warnings!
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div className={styles.warningsList}>
|
||||
{props.warnings.map((warning) => (
|
||||
<WarningListItem warning={warning} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function WarningListItem(props: { warning: EditorWarning }) {
|
||||
return (
|
||||
<div className={clsx(styles.warningItem, styles[`warning-item--${props.warning.severity.toLowerCase()}`],)}>
|
||||
<div className={styles.description}>
|
||||
{props.warning.description}
|
||||
</div>
|
||||
|
||||
<div className={styles.meta}>
|
||||
{props.warning.scope.id}
|
||||
{props.warning.scope.handleId && (
|
||||
<span className={styles.handle}>@{props.warning.scope.handleId}</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import {useEffect} from "react";
|
||||
import { Toolbar } from '../components/NodeComponents';
|
||||
import styles from '../../VisProg.module.css';
|
||||
import {SingleConnectionHandle} from "../components/RuleBasedHandle.tsx";
|
||||
import type {EditorWarning} from "../EditorWarnings.tsx";
|
||||
import type {EditorWarning} from "../components/EditorWarnings.tsx";
|
||||
import {allowOnlyConnectionsFromHandle} from "../HandleRules.ts";
|
||||
import useFlowStore from "../VisProgStores.tsx";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user