remove dependency on MUI
This commit is contained in:
parent
74f4c3bead
commit
3e192f8e26
8 changed files with 72 additions and 44 deletions
2
bun.lock
2
bun.lock
|
|
@ -4,10 +4,8 @@
|
||||||
"": {
|
"": {
|
||||||
"name": "bun-react-template",
|
"name": "bun-react-template",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/styled": "^11.14.1",
|
|
||||||
"@fontsource/roboto": "^5.2.8",
|
"@fontsource/roboto": "^5.2.8",
|
||||||
"@mui/icons-material": "^7.3.4",
|
"@mui/icons-material": "^7.3.4",
|
||||||
"@mui/material": "^7.3.4",
|
|
||||||
"react": "^19",
|
"react": "^19",
|
||||||
"react-dom": "^19",
|
"react-dom": "^19",
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,8 @@
|
||||||
"start": "NODE_ENV=production bun src/index.tsx"
|
"start": "NODE_ENV=production bun src/index.tsx"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/styled": "^11.14.1",
|
|
||||||
"@fontsource/roboto": "^5.2.8",
|
"@fontsource/roboto": "^5.2.8",
|
||||||
"@mui/icons-material": "^7.3.4",
|
"@mui/icons-material": "^7.3.4",
|
||||||
"@mui/material": "^7.3.4",
|
|
||||||
"react": "^19",
|
"react": "^19",
|
||||||
"react-dom": "^19"
|
"react-dom": "^19"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -94,3 +94,13 @@ button.active {
|
||||||
max-height: 100vh;
|
max-height: 100vh;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.stackVertical {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.stackHorizontal {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,6 @@ import { getSimTime, getWallClkDelay, TimeMode } from "../statecharts/time";
|
||||||
import "../index.css";
|
import "../index.css";
|
||||||
import "./App.css";
|
import "./App.css";
|
||||||
|
|
||||||
import Stack from "@mui/material/Stack";
|
|
||||||
import Box from "@mui/material/Box";
|
|
||||||
import { TopPanel } from "./TopPanel";
|
import { TopPanel } from "./TopPanel";
|
||||||
import { ShowAST, ShowInputEvents, ShowInternalEvents, ShowOutputEvents } from "./ShowAST";
|
import { ShowAST, ShowInputEvents, ShowInternalEvents, ShowOutputEvents } from "./ShowAST";
|
||||||
import { parseStatechart } from "../statecharts/parser";
|
import { parseStatechart } from "../statecharts/parser";
|
||||||
|
|
@ -92,6 +90,7 @@ export function App() {
|
||||||
console.log('recovering state...');
|
console.log('recovering state...');
|
||||||
const compressedState = window.location.hash.slice(1);
|
const compressedState = window.location.hash.slice(1);
|
||||||
if (compressedState.length === 0) {
|
if (compressedState.length === 0) {
|
||||||
|
// empty URL hash
|
||||||
console.log("no state to recover");
|
console.log("no state to recover");
|
||||||
setEditHistory(() => ({current: emptyState, history: [], future: []}));
|
setEditHistory(() => ({current: emptyState, history: [], future: []}));
|
||||||
return;
|
return;
|
||||||
|
|
@ -100,6 +99,7 @@ export function App() {
|
||||||
try {
|
try {
|
||||||
compressedBuffer = Uint8Array.fromBase64(compressedState); // may throw
|
compressedBuffer = Uint8Array.fromBase64(compressedState); // may throw
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
// probably invalid base64
|
||||||
console.error("failed to recover state:", e);
|
console.error("failed to recover state:", e);
|
||||||
setEditHistory(() => ({current: emptyState, history: [], future: []}));
|
setEditHistory(() => ({current: emptyState, history: [], future: []}));
|
||||||
return;
|
return;
|
||||||
|
|
@ -114,6 +114,7 @@ export function App() {
|
||||||
setEditHistory(() => ({current: recoveredState, history: [], future: []}));
|
setEditHistory(() => ({current: recoveredState, history: [], future: []}));
|
||||||
})
|
})
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
|
// any other error: invalid JSON, or decompression failed.
|
||||||
console.error("failed to recover state:", e);
|
console.error("failed to recover state:", e);
|
||||||
setEditHistory({current: emptyState, history: [], future: []});
|
setEditHistory({current: emptyState, history: [], future: []});
|
||||||
});
|
});
|
||||||
|
|
@ -370,16 +371,16 @@ export function App() {
|
||||||
</div>
|
</div>
|
||||||
</div>}
|
</div>}
|
||||||
|
|
||||||
<Stack sx={{height:'100%'}}>
|
<div className="stackVertical" style={{height:'100%'}}>
|
||||||
<Stack direction="row" sx={{flexGrow:1, overflow: "auto"}}>
|
<div className="stackHorizontal" style={{flexGrow:1, overflow: "auto"}}>
|
||||||
|
|
||||||
{/* Left: top bar and main editor */}
|
{/* Left: top bar and main editor */}
|
||||||
<Box sx={{flexGrow:1, overflow: "auto"}}>
|
<div style={{flexGrow:1, overflow: "auto"}}>
|
||||||
<Stack sx={{height:'100%'}}>
|
<div style={{height:'100%'}}>
|
||||||
{/* Top bar */}
|
{/* Top bar */}
|
||||||
<Box
|
<div
|
||||||
className="shadowBelow"
|
className="shadowBelow"
|
||||||
sx={{
|
style={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
borderBottom: 1,
|
borderBottom: 1,
|
||||||
borderColor: "divider",
|
borderColor: "divider",
|
||||||
|
|
@ -390,16 +391,16 @@ export function App() {
|
||||||
{editHistory && <TopPanel
|
{editHistory && <TopPanel
|
||||||
{...{trace, time, setTime, onUndo, onRedo, onInit, onClear, onBack, insertMode, setInsertMode, setModal, zoom, setZoom, showKeys, setShowKeys, editHistory}}
|
{...{trace, time, setTime, onUndo, onRedo, onInit, onClear, onBack, insertMode, setInsertMode, setModal, zoom, setZoom, showKeys, setShowKeys, editHistory}}
|
||||||
/>}
|
/>}
|
||||||
</Box>
|
</div>
|
||||||
{/* Below the top bar: Editor */}
|
{/* Below the top bar: Editor */}
|
||||||
<Box sx={{flexGrow:1, overflow: "auto"}}>
|
<div style={{flexGrow:1, overflow: "auto"}}>
|
||||||
{editorState && conns && syntaxErrors && <VisualEditor {...{state: editorState, setState: setEditorState, conns, trace, setTrace, syntaxErrors, insertMode, highlightActive, highlightTransitions, setModal, makeCheckPoint, zoom}}/>}
|
{editorState && conns && syntaxErrors && <VisualEditor {...{state: editorState, setState: setEditorState, conns, trace, setTrace, syntaxErrors, insertMode, highlightActive, highlightTransitions, setModal, makeCheckPoint, zoom}}/>}
|
||||||
</Box>
|
</div>
|
||||||
</Stack>
|
</div>
|
||||||
</Box>
|
</div>
|
||||||
|
|
||||||
{/* Right: sidebar */}
|
{/* Right: sidebar */}
|
||||||
<Box sx={{
|
<div style={{
|
||||||
borderLeft: 1,
|
borderLeft: 1,
|
||||||
borderColor: "divider",
|
borderColor: "divider",
|
||||||
flex: '0 0 content',
|
flex: '0 0 content',
|
||||||
|
|
@ -407,10 +408,10 @@ export function App() {
|
||||||
overflowX: "visible",
|
overflowX: "visible",
|
||||||
maxWidth: 'min(300px, 30vw)',
|
maxWidth: 'min(300px, 30vw)',
|
||||||
}}>
|
}}>
|
||||||
<Stack sx={{height:'100%'}}>
|
<div className="stackVertical" style={{height:'100%'}}>
|
||||||
<Box
|
<div
|
||||||
className={showExecutionTrace ? "shadowBelow" : ""}
|
className={showExecutionTrace ? "shadowBelow" : ""}
|
||||||
sx={{flex: '0 0 content', backgroundColor: ''}}
|
style={{flex: '0 0 content', backgroundColor: ''}}
|
||||||
>
|
>
|
||||||
<PersistentDetails localStorageKey="showStateTree" initiallyOpen={true}>
|
<PersistentDetails localStorageKey="showStateTree" initiallyOpen={true}>
|
||||||
<summary>state tree</summary>
|
<summary>state tree</summary>
|
||||||
|
|
@ -447,36 +448,34 @@ export function App() {
|
||||||
plant.render(trace.trace[trace.idx].plantState, event => onRaise(event.name, event.param))}
|
plant.render(trace.trace[trace.idx].plantState, event => onRaise(event.name, event.param))}
|
||||||
</PersistentDetails>
|
</PersistentDetails>
|
||||||
<details open={showExecutionTrace} onToggle={e => setShowExecutionTrace(e.newState === "open")}><summary>execution trace</summary></details>
|
<details open={showExecutionTrace} onToggle={e => setShowExecutionTrace(e.newState === "open")}><summary>execution trace</summary></details>
|
||||||
</Box>
|
</div>
|
||||||
|
|
||||||
|
{/* We cheat a bit, and render the execution trace depending on whether the <details> above is 'open' or not, rather than putting it as a child of the <details>. We do this because only then can we get the execution trace to scroll without the rest scrolling as well. */}
|
||||||
{showExecutionTrace &&
|
{showExecutionTrace &&
|
||||||
<Box sx={{
|
<div style={{
|
||||||
flexGrow:1,
|
flexGrow:1,
|
||||||
overflow:'auto',
|
overflow:'auto',
|
||||||
minHeight: '50vh',
|
minHeight: '50vh',
|
||||||
// minHeight: '75%', // <-- allows us to always scroll down the sidebar far enough such that the execution history is enough in view
|
// minHeight: '75%', // <-- allows us to always scroll down the sidebar far enough such that the execution history is enough in view
|
||||||
}}>
|
}}>
|
||||||
{/* <PersistentDetails localStorageKey="showExecutionTrace" initiallyOpen={true}> */}
|
<div ref={refRightSideBar}>
|
||||||
{/* <summary>execution trace</summary> */}
|
{ast && <RTHistory {...{ast, trace, setTrace, setTime}}/>}
|
||||||
<div ref={refRightSideBar}>
|
</div>
|
||||||
{ast && <RTHistory {...{ast, trace, setTrace, setTime}}/>}
|
</div>}
|
||||||
</div>
|
|
||||||
{/* </PersistentDetails> */}
|
|
||||||
</Box>}
|
|
||||||
|
|
||||||
<Box sx={{flex: '0 0 content'}}>
|
<div style={{flex: '0 0 content'}}>
|
||||||
</Box>
|
</div>
|
||||||
</Stack>
|
</div>
|
||||||
</Box>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</Stack>
|
</div>
|
||||||
|
|
||||||
{/* Bottom panel */}
|
{/* Bottom panel */}
|
||||||
<Box sx={{flex: '0 0 content'}}>
|
<div style={{flex: '0 0 content'}}>
|
||||||
{syntaxErrors && <BottomPanel {...{errors: syntaxErrors}}/>}
|
{syntaxErrors && <BottomPanel {...{errors: syntaxErrors}}/>}
|
||||||
</Box>
|
</div>
|
||||||
</Stack>
|
</div>
|
||||||
</>;
|
</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,16 @@ import { TraceableError } from "../statecharts/parser";
|
||||||
|
|
||||||
import "./BottomPanel.css";
|
import "./BottomPanel.css";
|
||||||
|
|
||||||
import head from "../head.svg" ;
|
import logo from "../../artwork/logo-playful.svg";
|
||||||
import { PersistentDetails } from "./PersistentDetails";
|
import { PersistentDetails } from "./PersistentDetails";
|
||||||
|
|
||||||
export function BottomPanel(props: {errors: TraceableError[]}) {
|
export function BottomPanel(props: {errors: TraceableError[]}) {
|
||||||
const [greeting, setGreeting] = useState(<><b><img src={head} style={{transform: "scaleX(-1)"}}/> "Welcome to StateBuddy, buddy!"</b><br/></>);
|
const [greeting, setGreeting] = useState(
|
||||||
|
<div style={{textAlign:'center'}}>
|
||||||
|
<span style={{fontSize: 18, fontStyle: 'italic'}}>
|
||||||
|
Welcome to <img src={logo} style={{maxWidth:'100%'}}/>
|
||||||
|
</span>
|
||||||
|
</div>);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ export type Connections = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function detectConnections(state: VisualEditorState): Connections {
|
export function detectConnections(state: VisualEditorState): Connections {
|
||||||
|
const startTime = performance.now();
|
||||||
// detect what is 'connected'
|
// detect what is 'connected'
|
||||||
const arrow2SideMap = new Map<string,[{ uid: string; part: RectSide; } | undefined, { uid: string; part: RectSide; } | undefined]>();
|
const arrow2SideMap = new Map<string,[{ uid: string; part: RectSide; } | undefined, { uid: string; part: RectSide; } | undefined]>();
|
||||||
const side2ArrowMap = new Map<string, Set<["start"|"end", string]>>();
|
const side2ArrowMap = new Map<string, Set<["start"|"end", string]>>();
|
||||||
|
|
@ -72,6 +73,11 @@ export function detectConnections(state: VisualEditorState): Connections {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const endTime = performance.now();
|
||||||
|
|
||||||
|
// rather slow, about 10ms for a large model:
|
||||||
|
// console.debug("connection detection took", endTime-startTime);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
arrow2SideMap,
|
arrow2SideMap,
|
||||||
side2ArrowMap,
|
side2ArrowMap,
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,8 @@ export function parseStatechart(state: VisualEditorState, conns: Connections): [
|
||||||
|
|
||||||
// step 1: figure out state hierarchy
|
// step 1: figure out state hierarchy
|
||||||
|
|
||||||
|
const startTime = performance.now();
|
||||||
|
|
||||||
// IMPORTANT ASSUMPTION: state.rountangles is sorted from big to small surface area:
|
// IMPORTANT ASSUMPTION: state.rountangles is sorted from big to small surface area:
|
||||||
for (const rt of state.rountangles) {
|
for (const rt of state.rountangles) {
|
||||||
const parent = findParent(rt);
|
const parent = findParent(rt);
|
||||||
|
|
@ -132,6 +134,11 @@ export function parseStatechart(state: VisualEditorState, conns: Connections): [
|
||||||
historyStates.push(historyState);
|
historyStates.push(historyState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const endTime = performance.now();
|
||||||
|
|
||||||
|
// currently seems to be quite fast:
|
||||||
|
// console.log('built state tree', endTime-startTime);
|
||||||
|
|
||||||
// step 2: figure out transitions
|
// step 2: figure out transitions
|
||||||
|
|
||||||
const transitions = new Map<string, Transition[]>();
|
const transitions = new Map<string, Transition[]>();
|
||||||
|
|
|
||||||
13
todo.txt
13
todo.txt
|
|
@ -27,8 +27,9 @@
|
||||||
|
|
||||||
TODO
|
TODO
|
||||||
|
|
||||||
- digital watch:
|
- testing
|
||||||
highlight when watch button pressed/released
|
use STL for testing
|
||||||
|
https://github.com/mvcisback/py-metric-temporal-logic
|
||||||
|
|
||||||
- maybe support:
|
- maybe support:
|
||||||
- explicit order of:
|
- explicit order of:
|
||||||
|
|
@ -51,17 +52,21 @@ TODO
|
||||||
don't crash and show the error
|
don't crash and show the error
|
||||||
- buttons to rotate selection 90 degrees
|
- buttons to rotate selection 90 degrees
|
||||||
|
|
||||||
|
- performance:
|
||||||
|
maybe try this for rendering the execution trace:
|
||||||
|
https://legacy.reactjs.org/docs/optimizing-performance.html#virtualize-long-lists
|
||||||
|
|
||||||
- experimental features:
|
- experimental features:
|
||||||
- multiverse execution history
|
- multiverse execution history
|
||||||
stable tree layout?
|
stable tree layout?
|
||||||
https://pub.dev/packages/ploeg_tree_layout
|
https://pub.dev/packages/ploeg_tree_layout
|
||||||
- local scopes
|
- local scopes
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for the assignment:
|
for the assignment:
|
||||||
*ALL* features
|
*ALL* features
|
||||||
add history (look at original Harel paper)
|
add history (look at original Harel paper)
|
||||||
|
add microwave oven
|
||||||
|
add traffic light
|
||||||
|
|
||||||
Publish StateBuddy paper(s):
|
Publish StateBuddy paper(s):
|
||||||
compare CS approach to other tools, not only YAKINDU
|
compare CS approach to other tools, not only YAKINDU
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue