diff --git a/bun.lock b/bun.lock index 53efbae..69f065c 100644 --- a/bun.lock +++ b/bun.lock @@ -4,6 +4,10 @@ "": { "name": "bun-react-template", "dependencies": { + "@emotion/styled": "^11.14.1", + "@fontsource/roboto": "^5.2.8", + "@mui/icons-material": "^7.3.4", + "@mui/material": "^7.3.4", "@nick/lz4": "npm:@jsr/nick__lz4", "react": "^19", "react-dom": "^19", @@ -16,26 +20,186 @@ }, }, "packages": { + "@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], + + "@babel/generator": ["@babel/generator@7.28.3", "", { "dependencies": { "@babel/parser": "^7.28.3", "@babel/types": "^7.28.2", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw=="], + + "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="], + + "@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="], + + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.27.1", "", {}, "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="], + + "@babel/parser": ["@babel/parser@7.28.4", "", { "dependencies": { "@babel/types": "^7.28.4" }, "bin": "./bin/babel-parser.js" }, "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg=="], + + "@babel/runtime": ["@babel/runtime@7.28.4", "", {}, "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ=="], + + "@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="], + + "@babel/traverse": ["@babel/traverse@7.28.4", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.4", "@babel/template": "^7.27.2", "@babel/types": "^7.28.4", "debug": "^4.3.1" } }, "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ=="], + + "@babel/types": ["@babel/types@7.28.4", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q=="], + + "@emotion/babel-plugin": ["@emotion/babel-plugin@11.13.5", "", { "dependencies": { "@babel/helper-module-imports": "^7.16.7", "@babel/runtime": "^7.18.3", "@emotion/hash": "^0.9.2", "@emotion/memoize": "^0.9.0", "@emotion/serialize": "^1.3.3", "babel-plugin-macros": "^3.1.0", "convert-source-map": "^1.5.0", "escape-string-regexp": "^4.0.0", "find-root": "^1.1.0", "source-map": "^0.5.7", "stylis": "4.2.0" } }, "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ=="], + + "@emotion/cache": ["@emotion/cache@11.14.0", "", { "dependencies": { "@emotion/memoize": "^0.9.0", "@emotion/sheet": "^1.4.0", "@emotion/utils": "^1.4.2", "@emotion/weak-memoize": "^0.4.0", "stylis": "4.2.0" } }, "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA=="], + + "@emotion/hash": ["@emotion/hash@0.9.2", "", {}, "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g=="], + + "@emotion/is-prop-valid": ["@emotion/is-prop-valid@1.4.0", "", { "dependencies": { "@emotion/memoize": "^0.9.0" } }, "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw=="], + + "@emotion/memoize": ["@emotion/memoize@0.9.0", "", {}, "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ=="], + + "@emotion/react": ["@emotion/react@11.14.0", "", { "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", "@emotion/cache": "^11.14.0", "@emotion/serialize": "^1.3.3", "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", "@emotion/utils": "^1.4.2", "@emotion/weak-memoize": "^0.4.0", "hoist-non-react-statics": "^3.3.1" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA=="], + + "@emotion/serialize": ["@emotion/serialize@1.3.3", "", { "dependencies": { "@emotion/hash": "^0.9.2", "@emotion/memoize": "^0.9.0", "@emotion/unitless": "^0.10.0", "@emotion/utils": "^1.4.2", "csstype": "^3.0.2" } }, "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA=="], + + "@emotion/sheet": ["@emotion/sheet@1.4.0", "", {}, "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg=="], + + "@emotion/styled": ["@emotion/styled@11.14.1", "", { "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", "@emotion/is-prop-valid": "^1.3.0", "@emotion/serialize": "^1.3.3", "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", "@emotion/utils": "^1.4.2" }, "peerDependencies": { "@emotion/react": "^11.0.0-rc.0", "react": ">=16.8.0" } }, "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw=="], + + "@emotion/unitless": ["@emotion/unitless@0.10.0", "", {}, "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg=="], + + "@emotion/use-insertion-effect-with-fallbacks": ["@emotion/use-insertion-effect-with-fallbacks@1.2.0", "", { "peerDependencies": { "react": ">=16.8.0" } }, "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg=="], + + "@emotion/utils": ["@emotion/utils@1.4.2", "", {}, "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA=="], + + "@emotion/weak-memoize": ["@emotion/weak-memoize@0.4.0", "", {}, "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg=="], + + "@fontsource/roboto": ["@fontsource/roboto@5.2.8", "", {}, "sha512-oh9g4Cg3loVMz9MWeKWfDI+ooxxG1aRVetkiKIb2ESS2rrryGecQ/y4pAj4z5A5ebyw450dYRi/c4k/I3UBhHA=="], + + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + + "@mui/core-downloads-tracker": ["@mui/core-downloads-tracker@7.3.4", "", {}, "sha512-BIktMapG3r4iXwIhYNpvk97ZfYWTreBBQTWjQKbNbzI64+ULHfYavQEX2w99aSWHS58DvXESWIgbD9adKcUOBw=="], + + "@mui/icons-material": ["@mui/icons-material@7.3.4", "", { "dependencies": { "@babel/runtime": "^7.28.4" }, "peerDependencies": { "@mui/material": "^7.3.4", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9n6Xcq7molXWYb680N2Qx+FRW8oT6j/LXF5PZFH3ph9X/Rct0B/BlLAsFI7iL9ySI6LVLuQIVtrLiPT82R7OZw=="], + + "@mui/material": ["@mui/material@7.3.4", "", { "dependencies": { "@babel/runtime": "^7.28.4", "@mui/core-downloads-tracker": "^7.3.4", "@mui/system": "^7.3.3", "@mui/types": "^7.4.7", "@mui/utils": "^7.3.3", "@popperjs/core": "^2.11.8", "@types/react-transition-group": "^4.4.12", "clsx": "^2.1.1", "csstype": "^3.1.3", "prop-types": "^15.8.1", "react-is": "^19.1.1", "react-transition-group": "^4.4.5" }, "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", "@mui/material-pigment-css": "^7.3.3", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/react", "@emotion/styled", "@mui/material-pigment-css", "@types/react"] }, "sha512-gEQL9pbJZZHT7lYJBKQCS723v1MGys2IFc94COXbUIyCTWa+qC77a7hUax4Yjd5ggEm35dk4AyYABpKKWC4MLw=="], + + "@mui/private-theming": ["@mui/private-theming@7.3.3", "", { "dependencies": { "@babel/runtime": "^7.28.4", "@mui/utils": "^7.3.3", "prop-types": "^15.8.1" }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-OJM+9nj5JIyPUvsZ5ZjaeC9PfktmK+W5YaVLToLR8L0lB/DGmv1gcKE43ssNLSvpoW71Hct0necfade6+kW3zQ=="], + + "@mui/styled-engine": ["@mui/styled-engine@7.3.3", "", { "dependencies": { "@babel/runtime": "^7.28.4", "@emotion/cache": "^11.14.0", "@emotion/serialize": "^1.3.3", "@emotion/sheet": "^1.4.0", "csstype": "^3.1.3", "prop-types": "^15.8.1" }, "peerDependencies": { "@emotion/react": "^11.4.1", "@emotion/styled": "^11.3.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/react", "@emotion/styled"] }, "sha512-CmFxvRJIBCEaWdilhXMw/5wFJ1+FT9f3xt+m2pPXhHPeVIbBg9MnMvNSJjdALvnQJMPw8jLhrUtXmN7QAZV2fw=="], + + "@mui/system": ["@mui/system@7.3.3", "", { "dependencies": { "@babel/runtime": "^7.28.4", "@mui/private-theming": "^7.3.3", "@mui/styled-engine": "^7.3.3", "@mui/types": "^7.4.7", "@mui/utils": "^7.3.3", "clsx": "^2.1.1", "csstype": "^3.1.3", "prop-types": "^15.8.1" }, "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/react", "@emotion/styled", "@types/react"] }, "sha512-Lqq3emZr5IzRLKaHPuMaLBDVaGvxoh6z7HMWd1RPKawBM5uMRaQ4ImsmmgXWtwJdfZux5eugfDhXJUo2mliS8Q=="], + + "@mui/types": ["@mui/types@7.4.7", "", { "dependencies": { "@babel/runtime": "^7.28.4" }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-8vVje9rdEr1rY8oIkYgP+Su5Kwl6ik7O3jQ0wl78JGSmiZhRHV+vkjooGdKD8pbtZbutXFVTWQYshu2b3sG9zw=="], + + "@mui/utils": ["@mui/utils@7.3.3", "", { "dependencies": { "@babel/runtime": "^7.28.4", "@mui/types": "^7.4.7", "@types/prop-types": "^15.7.15", "clsx": "^2.1.1", "prop-types": "^15.8.1", "react-is": "^19.1.1" }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-kwNAUh7bLZ7mRz9JZ+6qfRnnxbE4Zuc+RzXnhSpRSxjTlSTj7b4JxRLXpG+MVtPVtqks5k/XC8No1Vs3x4Z2gg=="], + "@nick/lz4": ["@jsr/nick__lz4@0.3.4", "https://npm.jsr.io/~/11/@jsr/nick__lz4/0.3.4.tgz", {}, "sha512-ZNc+8lCMC8D/cIa9GrSxRcEQC/MyThBOXXlg6rhrvAWSUcKPODwvscsVA+v1UugiBzfJ2dvQIZ/j8484PMadkg=="], + "@popperjs/core": ["@popperjs/core@2.11.8", "", {}, "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="], + "@types/bun": ["@types/bun@1.2.23", "", { "dependencies": { "bun-types": "1.2.23" } }, "sha512-le8ueOY5b6VKYf19xT3McVbXqLqmxzPXHsQT/q9JHgikJ2X22wyTW3g3ohz2ZMnp7dod6aduIiq8A14Xyimm0A=="], "@types/node": ["@types/node@24.6.2", "", { "dependencies": { "undici-types": "~7.13.0" } }, "sha512-d2L25Y4j+W3ZlNAeMKcy7yDsK425ibcAOO2t7aPTz6gNMH0z2GThtwENCDc0d/Pw9wgyRqE5Px1wkV7naz8ang=="], + "@types/parse-json": ["@types/parse-json@4.0.2", "", {}, "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="], + + "@types/prop-types": ["@types/prop-types@15.7.15", "", {}, "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw=="], + "@types/react": ["@types/react@19.2.0", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-1LOH8xovvsKsCBq1wnT4ntDUdCJKmnEakhsuoUSy6ExlHCkGP2hqnatagYTgFk6oeL0VU31u7SNjunPN+GchtA=="], "@types/react-dom": ["@types/react-dom@19.2.0", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-brtBs0MnE9SMx7px208g39lRmC5uHZs96caOJfTjFcYSLHNamvaSMfJNagChVNkup2SdtOxKX1FDBkRSJe1ZAg=="], + "@types/react-transition-group": ["@types/react-transition-group@4.4.12", "", { "peerDependencies": { "@types/react": "*" } }, "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w=="], + + "babel-plugin-macros": ["babel-plugin-macros@3.1.0", "", { "dependencies": { "@babel/runtime": "^7.12.5", "cosmiconfig": "^7.0.0", "resolve": "^1.19.0" } }, "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg=="], + "bun-types": ["bun-types@1.2.23", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-R9f0hKAZXgFU3mlrA0YpE/fiDvwV0FT9rORApt2aQVWSuJDzZOyB5QLc0N/4HF57CS8IXJ6+L5E4W1bW6NS2Aw=="], + "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], + + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + + "convert-source-map": ["convert-source-map@1.9.0", "", {}, "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="], + + "cosmiconfig": ["cosmiconfig@7.1.0", "", { "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", "parse-json": "^5.0.0", "path-type": "^4.0.0", "yaml": "^1.10.0" } }, "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA=="], + "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "dom-helpers": ["dom-helpers@5.2.1", "", { "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" } }, "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA=="], + + "error-ex": ["error-ex@1.3.4", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ=="], + + "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], + + "find-root": ["find-root@1.1.0", "", {}, "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + + "hoist-non-react-statics": ["hoist-non-react-statics@3.3.2", "", { "dependencies": { "react-is": "^16.7.0" } }, "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw=="], + + "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], + + "is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="], + + "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], + + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], + + "json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="], + + "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], + + "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + + "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], + + "parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="], + + "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], + + "path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], + "react": ["react@19.2.0", "", {}, "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ=="], "react-dom": ["react-dom@19.2.0", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.0" } }, "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ=="], + "react-is": ["react-is@19.2.0", "", {}, "sha512-x3Ax3kNSMIIkyVYhWPyO09bu0uttcAIoecO/um/rKGQ4EltYWVYtyiGkS/3xMynrbVQdS69Jhlv8FXUEZehlzA=="], + + "react-transition-group": ["react-transition-group@4.4.5", "", { "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", "loose-envify": "^1.4.0", "prop-types": "^15.6.2" }, "peerDependencies": { "react": ">=16.6.0", "react-dom": ">=16.6.0" } }, "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g=="], + + "resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="], + + "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], + "source-map": ["source-map@0.5.7", "", {}, "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="], + + "stylis": ["stylis@4.2.0", "", {}, "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw=="], + + "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], + "undici-types": ["undici-types@7.13.0", "", {}, "sha512-Ov2Rr9Sx+fRgagJ5AX0qvItZG/JKKoBRAVITs1zk7IqZGTJUwgUr7qoYBpWwakpWilTZFM98rG/AFRocu10iIQ=="], + + "yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="], + + "hoist-non-react-statics/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], + + "prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], } } diff --git a/package.json b/package.json index b1ba707..9f1d687 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,10 @@ "start": "NODE_ENV=production bun src/index.tsx" }, "dependencies": { + "@emotion/styled": "^11.14.1", + "@fontsource/roboto": "^5.2.8", + "@mui/icons-material": "^7.3.4", + "@mui/material": "^7.3.4", "@nick/lz4": "npm:@jsr/nick__lz4", "react": "^19", "react-dom": "^19" diff --git a/src/App/AST.tsx b/src/App/AST.tsx new file mode 100644 index 0000000..7b7ea3e --- /dev/null +++ b/src/App/AST.tsx @@ -0,0 +1,58 @@ +import { ConcreteState, stateDescription, Transition } from "../statecharts/abstract_syntax"; +import { Action, Expression } from "../statecharts/label_ast"; + +export function ShowTransition(props: {transition: Transition}) { + return <>➔ {stateDescription(props.transition.tgt)}; +} + +export function ShowExpr(props: {expr: Expression}) { + if (props.expr.kind === "literal") { + return <>{props.expr.value}; + } + else if (props.expr.kind === "ref") { + return <>{props.expr.variable}; + } + else if (props.expr.kind === "unaryExpr") { + return <>{props.expr.operator}; + } + else if (props.expr.kind === "binaryExpr") { + return <>{props.expr.operator}; + } +} + +export function ShowAction(props: {action: Action}) { + if (props.action.kind === "raise") { + return <>^{props.action.event}; + } + else if (props.action.kind === "assignment") { + return <>{props.action.lhs} = ;; + } +} + +export function AST(props: {root: ConcreteState, transitions: Map}) { + const description = stateDescription(props.root); + const outgoing = props.transitions.get(props.root.uid) || []; + + return
+ {props.root.kind}: {description} + + {props.root.entryActions.length>0 && + props.root.entryActions.map(action => +
 entry /
+ ) + } + {props.root.exitActions.length>0 && + props.root.exitActions.map(action => +
 exit /
+ ) + } + {props.root.children.length>0 && + props.root.children.map(child => + + ) + } + {outgoing.length>0 && + outgoing.map(transition => <> 
) + } +
+} diff --git a/src/App/App.css b/src/App/App.css index ccfe175..edd85c6 100644 --- a/src/App/App.css +++ b/src/App/App.css @@ -1,4 +1,4 @@ -.layoutVertical { +/* .layoutVertical { display: flex; flex-direction: column; width: 100%; @@ -21,10 +21,22 @@ .content { flex: 1 1 auto; overflow: auto; -} +} */ details { - padding-left :10; + padding-left: 20; + /* margin-left: 30; */ +} +summary { + margin-left: -20; +} + +.runtimeState { + padding-left: 4px; + padding-right: 4px; + padding-top: 2px; + padding-bottom: 2px; + margin-top: 2px; } .runtimeState:hover { @@ -47,3 +59,13 @@ details { background-color:"#eee"; text-align: "right"; } + + + +.toolbar > * { + vertical-align: middle; + height: 26px; +} +.toolbar > input { + height: 20px; +} \ No newline at end of file diff --git a/src/App/App.tsx b/src/App/App.tsx index 91e0daf..fb3c5bf 100644 --- a/src/App/App.tsx +++ b/src/App/App.tsx @@ -1,100 +1,29 @@ import { useEffect, useState } from "react"; -import { ConcreteState, emptyStatechart, Statechart, stateDescription, Transition } from "../VisualEditor/ast"; -import { handleInputEvent, initialize } from "../VisualEditor/interpreter"; -import { TimerElapseEvent, Timers } from "@/VisualEditor/runtime_types"; -import { Action, Expression } from "../VisualEditor/label_ast"; -import { BigStep, BigStepOutput, Environment, Mode } from "../VisualEditor/runtime_types"; +import { emptyStatechart, Statechart } from "../statecharts/abstract_syntax"; +import { handleInputEvent, initialize } from "../statecharts/interpreter"; +import { BigStep, BigStepOutput } from "../statecharts/runtime_types"; import { VisualEditor } from "../VisualEditor/VisualEditor"; -import { getSimTime, getWallClkDelay, setPaused, setRealtime, TimeMode } from "../VisualEditor/time"; +import { getSimTime, getWallClkDelay, TimeMode } from "../statecharts/time"; import "../index.css"; import "./App.css"; -export function ShowTransition(props: {transition: Transition}) { - return <>➔ {stateDescription(props.transition.tgt)}; -} - -export function ShowExpr(props: {expr: Expression}) { - if (props.expr.kind === "literal") { - return <>{props.expr.value}; - } - else if (props.expr.kind === "ref") { - return <>{props.expr.variable}; - } - else if (props.expr.kind === "unaryExpr") { - return <>{props.expr.operator}; - } - else if (props.expr.kind === "binaryExpr") { - return <>{props.expr.operator}; - } -} - -export function ShowAction(props: {action: Action}) { - if (props.action.kind === "raise") { - return <>^{props.action.event}; - } - else if (props.action.kind === "assignment") { - return <>{props.action.lhs} = ;; - } -} - -export function AST(props: {root: ConcreteState, transitions: Map}) { - const description = stateDescription(props.root); - const outgoing = props.transitions.get(props.root.uid) || []; - - return
- {props.root.kind}: {description} - - {props.root.entryActions.length>0 && - props.root.entryActions.map(action => -
 entry /
- ) - } - {props.root.exitActions.length>0 && - props.root.exitActions.map(action => -
 exit /
- ) - } - {props.root.children.length>0 && - props.root.children.map(child => - - ) - } - {outgoing.length>0 && - outgoing.map(transition => <> 
) - } -
-} - - -function formatTime(timeMs: number) { - const leadingZeros = "00" + Math.floor(timeMs) % 1000; - const formatted = `${Math.floor(timeMs / 1000)}.${(leadingZeros).substring(leadingZeros.length-3)}`; - return formatted; -} - -function compactTime(timeMs: number) { - if (timeMs % 1000 === 0) { - return `${timeMs / 1000}s`; - } - return `${timeMs} ms`; -} - +import { Box, Stack } from "@mui/material"; +import { TopPanel } from "./TopPanel"; +import { RTHistory } from "./RTHistory"; +import { AST } from "./AST"; export function App() { const [ast, setAST] = useState(emptyStatechart); const [errors, setErrors] = useState<[string,string][]>([]); const [rt, setRT] = useState([]); - const [rtIdx, setRTIdx] = useState(null); + const [rtIdx, setRTIdx] = useState(); const [time, setTime] = useState({kind: "paused", simtime: 0}); - const [timescale, setTimescale] = useState(1); - const [displayTime, setDisplayTime] = useState("0.000"); - - function restart() { + function onInit() { const config = initialize(ast); console.log('runtime: ', rt); setRT([{inputEvent: null, simtime: 0, ...config}]); @@ -102,14 +31,14 @@ export function App() { setTime({kind: "paused", simtime: 0}); } - function clear() { + function onClear() { setRT([]); - setRTIdx(null); + setRTIdx(undefined); setTime({kind: "paused", simtime: 0}); } - function raise(inputEvent: string) { - if (rt.length>0 && rtIdx!==null && ast.inputEvents.has(inputEvent)) { + function onRaise(inputEvent: string) { + if (rt.length>0 && rtIdx!==undefined && ast.inputEvents.has(inputEvent)) { const simtime = getSimTime(time, performance.now()); const nextConfig = handleInputEvent(simtime, inputEvent, ast, rt[rtIdx]!); appendNewConfig(inputEvent, simtime, nextConfig); @@ -121,19 +50,9 @@ export function App() { setRTIdx(rtIdx!+1); } - function updateDisplayedTime() { - const now = performance.now(); - const timeMs = getSimTime(time, now); - setDisplayTime(formatTime(timeMs)); - } - useEffect(() => { - const interval = setInterval(() => { - updateDisplayedTime(); - }, 20); - let timeout: NodeJS.Timeout | undefined; - if (rtIdx !== null) { + if (rtIdx !== undefined) { const currentRt = rt[rtIdx]!; const timers = currentRt.environment.get("_timers") || []; if (timers.length > 0) { @@ -156,135 +75,45 @@ export function App() { } return () => { - clearInterval(interval); if (timeout) clearTimeout(timeout); } }, [time, rtIdx]); - function onChangePaused(paused: boolean, wallclktime: number) { - setTime(time => { - if (paused) { - return setPaused(time, performance.now()); - } - else { - return setRealtime(time, timescale, wallclktime); - } - }); - updateDisplayedTime(); - } - - function onTimeScaleChange(newValue: string, wallclktime: number) { - const asFloat = parseFloat(newValue); - if (Number.isNaN(asFloat)) { - return; - } - setTimescale(asFloat); - setTime(time => { - if (time.kind === "paused") { - return time; - } - else { - return setRealtime(time, asFloat, wallclktime); - } - }) - } - - function gotoRt(idx: number, timestamp: number) { - setRTIdx(idx); - setTime({kind: "paused", simtime: timestamp}); - } - - // timestamp of next timed transition, in simulated time - const timers: Timers = (rt[rtIdx!]?.environment.get("_timers") || []); - const nextTimedTransition: [number, TimerElapseEvent] | undefined = timers[0]; - - return
-
- -
-
- - -   - {ast.inputEvents && - <>raise  - {[...ast.inputEvents].map(event => )} -   - } - onChangePaused(e.target.checked, performance.now())}/> - - onChangePaused(!e.target.checked, performance.now())}/> - -   -   - onTimeScaleChange(e.target.value, performance.now())}/> -   -   - - {nextTimedTransition && - <> -   -   - - - - } -
- -
-
+ return + {/* Top bar */} + + + + + {/* main */} + -
- -
-
; -} - -function ShowEnvironment(props: {environment: Environment}) { - return
{[...props.environment.entries()] - .filter(([variable]) => !variable.startsWith('_')) - .map(([variable,value]) => - `${variable}: ${value}` - ).join(', ')}
; -} - -function ShowMode(props: {mode: Mode, statechart: Statechart}) { - const activeLeafs = getActiveLeafs(props.mode, props.statechart); - return
mode: {[...activeLeafs].map(uid => - stateDescription(props.statechart.uid2State.get(uid)!)).join(", ")}
; -} - -function getActiveLeafs(mode: Mode, sc: Statechart) { - const toDelete = []; - for (const stateA of mode) { - for (const stateB of mode) { - if (sc.uid2State.get(stateA)!.parent === sc.uid2State.get(stateB)) { - toDelete.push(stateB); - } - } - } - return mode.difference(new Set(toDelete)); + + {/* right sidebar */} + + +
+ +
+ + ; } export default App; diff --git a/src/App/RTHistory.tsx b/src/App/RTHistory.tsx new file mode 100644 index 0000000..3b65402 --- /dev/null +++ b/src/App/RTHistory.tsx @@ -0,0 +1,57 @@ +import { Dispatch, SetStateAction } from "react"; +import { Statechart, stateDescription } from "../statecharts/abstract_syntax"; +import { BigStep, Environment, Mode } from "../statecharts/runtime_types"; +import { formatTime } from "./util"; +import { TimeMode } from "../statecharts/time"; + +type RTHistoryProps = { + rt: BigStep[], + rtIdx: number | undefined, + ast: Statechart, + setRTIdx: Dispatch>, + setTime: Dispatch>, +} + +export function RTHistory({rt, rtIdx, ast, setRTIdx, setTime}: RTHistoryProps) { + function gotoRt(idx: number, timestamp: number) { + setRTIdx(idx); + setTime({kind: "paused", simtime: timestamp}); + } + + return rt.map((rt, idx) => <> +
gotoRt(idx, rt.simtime)}> +
({formatTime(rt.simtime)}, {rt.inputEvent || ""})
+ + + {rt.outputEvents.length>0 &&
+ {rt.outputEvents.map((e:string) => '^'+e).join(', ')} +
} +
); +} + + +function ShowEnvironment(props: {environment: Environment}) { + return
{[...props.environment.entries()] + .filter(([variable]) => !variable.startsWith('_')) + .map(([variable,value]) => + `${variable}: ${value}` + ).join(', ')}
; +} + +function ShowMode(props: {mode: Mode, statechart: Statechart}) { + const activeLeafs = getActiveLeafs(props.mode, props.statechart); + return
mode: {[...activeLeafs].map(uid => + stateDescription(props.statechart.uid2State.get(uid)!)).join(", ")}
; +} + +function getActiveLeafs(mode: Mode, sc: Statechart) { + const toDelete = []; + for (const stateA of mode) { + for (const stateB of mode) { + if (sc.uid2State.get(stateA)!.parent === sc.uid2State.get(stateB)) { + toDelete.push(stateB); + } + } + } + return mode.difference(new Set(toDelete)); +} diff --git a/src/App/TopPanel.tsx b/src/App/TopPanel.tsx new file mode 100644 index 0000000..a11a77f --- /dev/null +++ b/src/App/TopPanel.tsx @@ -0,0 +1,127 @@ +import { Dispatch, SetStateAction, useEffect, useState } from "react"; +import { BigStep, TimerElapseEvent, Timers } from "../statecharts/runtime_types"; +import { getSimTime, setPaused, setRealtime, TimeMode } from "../statecharts/time"; +import { Statechart } from "../statecharts/abstract_syntax"; + +import CachedIcon from '@mui/icons-material/Cached'; +import ClearIcon from '@mui/icons-material/Clear'; +import PauseIcon from '@mui/icons-material/Pause'; +import PlayArrowIcon from '@mui/icons-material/PlayArrow'; +import BoltIcon from '@mui/icons-material/Bolt'; +import SkipNextIcon from '@mui/icons-material/SkipNext'; +import { formatTime } from "./util"; + +export type TopPanelProps = { + rt?: BigStep, + time: TimeMode, + setTime: Dispatch>, + onInit: () => void, + onClear: () => void, + onRaise: (e: string) => void, + ast: Statechart, +} + +export function TopPanel({rt, time, setTime, onInit, onClear, onRaise, ast}: TopPanelProps) { + const [displayTime, setDisplayTime] = useState("0.000"); + const [timescale, setTimescale] = useState(1); + + function updateDisplayedTime() { + const now = performance.now(); + const timeMs = getSimTime(time, now); + setDisplayTime(formatTime(timeMs)); + } + + useEffect(() => { + const interval = setInterval(() => { + updateDisplayedTime(); + }, 20); + return () => { + clearInterval(interval); + } + }, [time]); + + function onChangePaused(paused: boolean, wallclktime: number) { + setTime(time => { + if (paused) { + return setPaused(time, performance.now()); + } + else { + return setRealtime(time, timescale, wallclktime); + } + }); + updateDisplayedTime(); + } + + function onTimeScaleChange(newValue: string, wallclktime: number) { + const asFloat = parseFloat(newValue); + if (Number.isNaN(asFloat)) { + return; + } + const maxed = Math.min(asFloat, 64); + const mined = Math.max(maxed, 1/64); + setTimescale(mined); + setTime(time => { + if (time.kind === "paused") { + return time; + } + else { + return setRealtime(time, mined, wallclktime); + } + }); + } + + // timestamp of next timed transition, in simulated time + const timers: Timers = (rt?.environment.get("_timers") || []); + const nextTimedTransition: [number, TimerElapseEvent] | undefined = timers[0]; + + return
+ + + +   + + + + +   + +   + + onTimeScaleChange(e.target.value, performance.now())}/> + + +   + + {ast.inputEvents && + <> + {[...ast.inputEvents].map(event => )} +   + } + + {/* onChangePaused(newValue==="paused", performance.now())} size="small"> + + + */} + +   + +   + + +   + +   + + +
; +} diff --git a/src/App/util.ts b/src/App/util.ts new file mode 100644 index 0000000..dd3136e --- /dev/null +++ b/src/App/util.ts @@ -0,0 +1,13 @@ +export function formatTime(timeMs: number) { + const leadingZeros = "00" + Math.floor(timeMs) % 1000; + const formatted = `${Math.floor(timeMs / 1000)}.${(leadingZeros).substring(leadingZeros.length-3)}`; + return formatted; +} + +export function compactTime(timeMs: number) { + if (timeMs % 1000 === 0) { + return `${timeMs / 1000}s`; + } + return `${timeMs} ms`; +} + diff --git a/src/VisualEditor/VisualEditor.css b/src/VisualEditor/VisualEditor.css index af97567..10003be 100644 --- a/src/VisualEditor/VisualEditor.css +++ b/src/VisualEditor/VisualEditor.css @@ -1,5 +1,6 @@ .svgCanvas { cursor: crosshair; + background-color: #eee; } text, text.highlight { @@ -102,6 +103,7 @@ text.highlight { .rountangle.or { stroke-dasharray: 7 6; + fill: #eee; } .arrow { diff --git a/src/VisualEditor/VisualEditor.tsx b/src/VisualEditor/VisualEditor.tsx index 77776d6..c5ba308 100644 --- a/src/VisualEditor/VisualEditor.tsx +++ b/src/VisualEditor/VisualEditor.tsx @@ -4,13 +4,13 @@ import { ArcDirection, Line2D, Rect2D, Vec2D, addV2D, arcDirection, area, euclid import "./VisualEditor.css"; import { getBBoxInSvgCoords } from "./svg_helper"; -import { VisualEditorState, Rountangle, emptyState, Arrow, ArrowPart, RountanglePart, findNearestRountangleSide, findNearestArrow, Text, findRountangle } from "./editor_types"; -import { parseStatechart } from "./parser"; +import { VisualEditorState, Rountangle, emptyState, Arrow, ArrowPart, RountanglePart, findNearestRountangleSide, findNearestArrow, Text, findRountangle } from "../statecharts/concrete_syntax"; +import { parseStatechart } from "../statecharts/parser"; import { CORNER_HELPER_OFFSET, CORNER_HELPER_RADIUS, MIN_ROUNTANGLE_SIZE, ROUNTANGLE_RADIUS } from "./parameters"; import * as lz4 from "@nick/lz4"; -import { RT_Statechart } from "./runtime_types"; -import { Statechart } from "./ast"; +import { BigStep, RT_Statechart } from "../statecharts/runtime_types"; +import { Statechart } from "../statecharts/abstract_syntax"; type DraggingState = { @@ -50,15 +50,13 @@ export const sides: [RountanglePart, (r:Rect2D)=>Line2D][] = [ ]; type VisualEditorProps = { - ast: Statechart, setAST: Dispatch>, - rt: RT_Statechart|null, - setRT: Dispatch>, + rt: BigStep|undefined, errors: [string,string][], setErrors: Dispatch>, }; -export function VisualEditor({ast, setAST, rt, setRT, errors, setErrors}: VisualEditorProps) { +export function VisualEditor({setAST, rt, errors, setErrors}: VisualEditorProps) { const [historyState, setHistoryState] = useState({current: emptyState, history: [], future: []}); const state = historyState.current; @@ -145,7 +143,7 @@ export function VisualEditor({ast, setAST, rt, setRT, errors, setErrors}: Visual window.location.hash = "#"+compressedStateString; const [statechart, errors] = parseStatechart(state); - console.log('statechart: ', statechart, 'errors:', errors); + // console.log('statechart: ', statechart, 'errors:', errors); setErrors(errors); setAST(statechart); }, 100); diff --git a/src/VisualEditor/geometry.ts b/src/VisualEditor/geometry.ts index a272284..35405cf 100644 --- a/src/VisualEditor/geometry.ts +++ b/src/VisualEditor/geometry.ts @@ -1,4 +1,4 @@ -import { RountanglePart } from "./editor_types"; +import { RountanglePart } from "../statecharts/concrete_syntax"; export type Vec2D = { x: number; diff --git a/src/index.html b/src/index.html index 6af91be..f33b953 100644 --- a/src/index.html +++ b/src/index.html @@ -3,7 +3,7 @@ - + StateBuddy diff --git a/src/logo.svg b/src/logo.svg deleted file mode 100644 index 7ef1500..0000000 --- a/src/logo.svg +++ /dev/null @@ -1 +0,0 @@ -Bun Logo \ No newline at end of file diff --git a/src/react.svg b/src/react.svg deleted file mode 100644 index 1ab815a..0000000 --- a/src/react.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/src/VisualEditor/ast.ts b/src/statecharts/abstract_syntax.ts similarity index 100% rename from src/VisualEditor/ast.ts rename to src/statecharts/abstract_syntax.ts diff --git a/src/VisualEditor/actionlang_interpreter.ts b/src/statecharts/actionlang_interpreter.ts similarity index 100% rename from src/VisualEditor/actionlang_interpreter.ts rename to src/statecharts/actionlang_interpreter.ts diff --git a/src/VisualEditor/editor_types.ts b/src/statecharts/concrete_syntax.ts similarity index 93% rename from src/VisualEditor/editor_types.ts rename to src/statecharts/concrete_syntax.ts index 22359c5..d550465 100644 --- a/src/VisualEditor/editor_types.ts +++ b/src/statecharts/concrete_syntax.ts @@ -1,6 +1,6 @@ -import { Rect2D, Vec2D, Line2D, euclideanDistance, intersectLines, isWithin, lineBBox, isEntirelyWithin } from "./geometry"; -import { ARROW_SNAP_THRESHOLD, TEXT_SNAP_THRESHOLD } from "./parameters"; -import { sides } from "./VisualEditor"; +import { Rect2D, Vec2D, Line2D, euclideanDistance, intersectLines, isWithin, lineBBox, isEntirelyWithin } from "../VisualEditor/geometry"; +import { ARROW_SNAP_THRESHOLD, TEXT_SNAP_THRESHOLD } from "../VisualEditor/parameters"; +import { sides } from "../VisualEditor/VisualEditor"; export type Rountangle = { uid: string; diff --git a/src/VisualEditor/interpreter.ts b/src/statecharts/interpreter.ts similarity index 99% rename from src/VisualEditor/interpreter.ts rename to src/statecharts/interpreter.ts index 34f4613..5b406fa 100644 --- a/src/VisualEditor/interpreter.ts +++ b/src/statecharts/interpreter.ts @@ -1,5 +1,5 @@ import { evalExpr } from "./actionlang_interpreter"; -import { computeArena, ConcreteState, getDescendants, isOverlapping, OrState, Statechart, stateDescription, Transition } from "./ast"; +import { computeArena, ConcreteState, getDescendants, isOverlapping, OrState, Statechart, stateDescription, Transition } from "./abstract_syntax"; import { Action } from "./label_ast"; import { Environment, RaisedEvents, Mode, RT_Statechart, initialRaised, BigStepOutput, TimerElapseEvent, Timers } from "./runtime_types"; diff --git a/src/VisualEditor/label_ast.ts b/src/statecharts/label_ast.ts similarity index 100% rename from src/VisualEditor/label_ast.ts rename to src/statecharts/label_ast.ts diff --git a/src/VisualEditor/label_parser.js b/src/statecharts/label_parser.js similarity index 100% rename from src/VisualEditor/label_parser.js rename to src/statecharts/label_parser.js diff --git a/src/VisualEditor/parser.ts b/src/statecharts/parser.ts similarity index 98% rename from src/VisualEditor/parser.ts rename to src/statecharts/parser.ts index f95c088..f968e23 100644 --- a/src/VisualEditor/parser.ts +++ b/src/statecharts/parser.ts @@ -1,6 +1,6 @@ -import { ConcreteState, OrState, Statechart, Transition } from "./ast"; -import { findNearestArrow, findNearestRountangleSide, findRountangle, Rountangle, VisualEditorState } from "./editor_types"; -import { isEntirelyWithin } from "./geometry"; +import { ConcreteState, OrState, Statechart, Transition } from "./abstract_syntax"; +import { findNearestArrow, findNearestRountangleSide, findRountangle, Rountangle, VisualEditorState } from "./concrete_syntax"; +import { isEntirelyWithin } from "../VisualEditor/geometry"; import { Action, Expression, ParsedText } from "./label_ast"; import { parse as parseLabel, SyntaxError } from "./label_parser"; diff --git a/src/VisualEditor/runtime_types.ts b/src/statecharts/runtime_types.ts similarity index 100% rename from src/VisualEditor/runtime_types.ts rename to src/statecharts/runtime_types.ts diff --git a/src/VisualEditor/time.ts b/src/statecharts/time.ts similarity index 100% rename from src/VisualEditor/time.ts rename to src/statecharts/time.ts diff --git a/src/VisualEditor/transition_label.grammar b/src/statecharts/transition_label.grammar similarity index 100% rename from src/VisualEditor/transition_label.grammar rename to src/statecharts/transition_label.grammar