diff --git a/crates/compiler/checkmate/www/.vim/coc-settings.json b/crates/compiler/checkmate/www/.vim/coc-settings.json new file mode 100644 index 00000000000..843796a4dc3 --- /dev/null +++ b/crates/compiler/checkmate/www/.vim/coc-settings.json @@ -0,0 +1,4 @@ +{ + "tsserver.useLocalTsdk": true, + "tsserver.tsdk": "${workspaceFolder}/node_modules/typescript/lib" +} \ No newline at end of file diff --git a/crates/compiler/checkmate/www/package-lock.json b/crates/compiler/checkmate/www/package-lock.json index 6661e3c7212..bb8b824b2cb 100644 --- a/crates/compiler/checkmate/www/package-lock.json +++ b/crates/compiler/checkmate/www/package-lock.json @@ -8,8 +8,10 @@ "name": "checkmate", "version": "0.1.0", "dependencies": { + "@dagrejs/dagre": "^1.0.2", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "reactflow": "^11.7.4" }, "devDependencies": { "@babel/plugin-proposal-private-property-in-object": "^7.21.11", @@ -17,9 +19,11 @@ "@types/node": "^16.18.38", "@types/react": "^18.2.15", "@types/react-dom": "^18.2.7", + "clsx": "^2.0.0", "json-schema-to-typescript": "^13.0.2", "react-scripts": "5.0.1", "tailwindcss": "^3.3.3", + "tiny-typed-emitter": "^2.1.0", "typescript": "^4.9.5" } }, @@ -2496,6 +2500,22 @@ "postcss-selector-parser": "^6.0.10" } }, + "node_modules/@dagrejs/dagre": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@dagrejs/dagre/-/dagre-1.0.2.tgz", + "integrity": "sha512-7N7vEZDlcU4uRHWuL/9RyI8IgM/d4ULR7z2exJALshh7BHF3tFjYL2pW6bQ4mmlDzd2Tr49KJMIY87Be1L6J0w==", + "dependencies": { + "@dagrejs/graphlib": "2.1.13" + } + }, + "node_modules/@dagrejs/graphlib": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/@dagrejs/graphlib/-/graphlib-2.1.13.tgz", + "integrity": "sha512-calbMa7Gcyo+/t23XBaqQqon8LlgE9regey4UVoikoenKBXvUnCUL3s9RP6USCxttfr0XWVICtYUuKMdehKqMw==", + "engines": { + "node": ">17.0.0" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -3546,6 +3566,102 @@ } } }, + "node_modules/@reactflow/background": { + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/@reactflow/background/-/background-11.2.4.tgz", + "integrity": "sha512-SYQbCRCU0GuxT/40Tm7ZK+l5wByGnNJSLtZhbL9C/Hl7JhsJXV3UGXr0vrlhVZUBEtkWA7XhZM/5S9XEA5XSFA==", + "dependencies": { + "@reactflow/core": "11.7.4", + "classcat": "^5.0.3", + "zustand": "^4.3.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/controls": { + "version": "11.1.15", + "resolved": "https://registry.npmjs.org/@reactflow/controls/-/controls-11.1.15.tgz", + "integrity": "sha512-//33XfBYu8vQ6brfmlZwKrDoh+8hh93xO2d88XiqfIbrPEEb32SYjsb9mS9VuHKNlSIW+eB27fBA1Gt00mEj5w==", + "dependencies": { + "@reactflow/core": "11.7.4", + "classcat": "^5.0.3", + "zustand": "^4.3.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/core": { + "version": "11.7.4", + "resolved": "https://registry.npmjs.org/@reactflow/core/-/core-11.7.4.tgz", + "integrity": "sha512-nt0T8ERp8TE7YCDQViaoEY9lb0StDPrWHVx3zBjhStFYET3wc88t8QRasZdf99xRTmyNtI3U3M40M5EBLNUpMw==", + "dependencies": { + "@types/d3": "^7.4.0", + "@types/d3-drag": "^3.0.1", + "@types/d3-selection": "^3.0.3", + "@types/d3-zoom": "^3.0.1", + "classcat": "^5.0.3", + "d3-drag": "^3.0.0", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0", + "zustand": "^4.3.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/minimap": { + "version": "11.5.4", + "resolved": "https://registry.npmjs.org/@reactflow/minimap/-/minimap-11.5.4.tgz", + "integrity": "sha512-1tDBj2zX2gxu2oHU6qvH5RGNrOWRfRxu8369KhDotuuBN5yJrGXJzWIKikwhzjsNsQJYOB+B0cS44yWAfwSwzw==", + "dependencies": { + "@reactflow/core": "11.7.4", + "@types/d3-selection": "^3.0.3", + "@types/d3-zoom": "^3.0.1", + "classcat": "^5.0.3", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0", + "zustand": "^4.3.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/node-resizer": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@reactflow/node-resizer/-/node-resizer-2.1.1.tgz", + "integrity": "sha512-5Q+IBmZfpp/bYsw3+KRVJB1nUbj6W3XAp5ycx4uNWH+K98vbssymyQsW0vvKkIhxEPg6tkiMzO4UWRWvwBwt1g==", + "dependencies": { + "@reactflow/core": "^11.6.0", + "classcat": "^5.0.4", + "d3-drag": "^3.0.0", + "d3-selection": "^3.0.0", + "zustand": "^4.3.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/node-toolbar": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@reactflow/node-toolbar/-/node-toolbar-1.2.3.tgz", + "integrity": "sha512-uFQy9xpog92s0G1wsPLniwV9nyH4i/MmL7QoMsWdnKaOi7XMhd8SJcCzUdHC3imR21HltsuQITff/XQ51ApMbg==", + "dependencies": { + "@reactflow/core": "11.7.4", + "classcat": "^5.0.3", + "zustand": "^4.3.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, "node_modules/@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -3985,6 +4101,228 @@ "@types/node": "*" } }, + "node_modules/@types/d3": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.0.tgz", + "integrity": "sha512-jIfNVK0ZlxcuRDKtRS/SypEyOQ6UHaFQBKv032X45VvxSJ6Yi5G9behy9h6tNTHTDGh5Vq+KbmBjUWLgY4meCA==", + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "node_modules/@types/d3-array": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.5.tgz", + "integrity": "sha512-Qk7fpJ6qFp+26VeQ47WY0mkwXaiq8+76RJcncDEfMc2ocRzXLO67bLFRNI4OX1aGBoPzsM5Y2T+/m1pldOgD+A==" + }, + "node_modules/@types/d3-axis": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.2.tgz", + "integrity": "sha512-uGC7DBh0TZrU/LY43Fd8Qr+2ja1FKmH07q2FoZFHo1eYl8aj87GhfVoY1saJVJiq24rp1+wpI6BvQJMKgQm8oA==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-brush": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.2.tgz", + "integrity": "sha512-2TEm8KzUG3N7z0TrSKPmbxByBx54M+S9lHoP2J55QuLU0VSQ9mE96EJSAOVNEqd1bbynMjeTS9VHmz8/bSw8rA==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-chord": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.2.tgz", + "integrity": "sha512-abT/iLHD3sGZwqMTX1TYCMEulr+wBd0SzyOQnjYNLp7sngdOHYtNkMRI5v3w5thoN+BWtlHVDx2Osvq6fxhZWw==" + }, + "node_modules/@types/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA==" + }, + "node_modules/@types/d3-contour": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.2.tgz", + "integrity": "sha512-k6/bGDoAGJZnZWaKzeB+9glgXCYGvh6YlluxzBREiVo8f/X2vpTEdgPy9DN7Z2i42PZOZ4JDhVdlTSTSkLDPlQ==", + "dependencies": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-delaunay": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.1.tgz", + "integrity": "sha512-tLxQ2sfT0p6sxdG75c6f/ekqxjyYR0+LwPrsO1mbC9YDBzPJhs2HbJJRrn8Ez1DBoHRo2yx7YEATI+8V1nGMnQ==" + }, + "node_modules/@types/d3-dispatch": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.2.tgz", + "integrity": "sha512-rxN6sHUXEZYCKV05MEh4z4WpPSqIw+aP7n9ZN6WYAAvZoEAghEK1WeVZMZcHRBwyaKflU43PCUAJNjFxCzPDjg==" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.2.tgz", + "integrity": "sha512-qmODKEDvyKWVHcWWCOVcuVcOwikLVsyc4q4EBJMREsoQnR2Qoc2cZQUyFUPgO9q4S3qdSqJKBsuefv+h0Qy+tw==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-76pBHCMTvPLt44wFOieouXcGXWOF0AJCceUvaFkxSZEu4VDUdv93JfpMa6VGNFs01FHfuP4a5Ou68eRG1KBfTw==" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.0.tgz", + "integrity": "sha512-aMo4eaAOijJjA6uU+GIeW018dvy9+oH5Y2VPPzjjfxevvGQ/oRDs+tfYC9b50Q4BygRR8yE2QCLsrT0WtAVseA==" + }, + "node_modules/@types/d3-fetch": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.2.tgz", + "integrity": "sha512-gllwYWozWfbep16N9fByNBDTkJW/SyhH6SGRlXloR7WdtAaBui4plTP+gbUgiEot7vGw/ZZop1yDZlgXXSuzjA==", + "dependencies": { + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.4.tgz", + "integrity": "sha512-q7xbVLrWcXvSBBEoadowIUJ7sRpS1yvgMWnzHJggFy5cUZBq2HZL5k/pBSm0GdYWS1vs5/EDwMjSKF55PDY4Aw==" + }, + "node_modules/@types/d3-format": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.1.tgz", + "integrity": "sha512-5KY70ifCCzorkLuIkDe0Z9YTf9RR2CjBX1iaJG+rgM/cPP+sO+q9YdQ9WdhQcgPj1EQiJ2/0+yUkkziTG6Lubg==" + }, + "node_modules/@types/d3-geo": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.0.3.tgz", + "integrity": "sha512-bK9uZJS3vuDCNeeXQ4z3u0E7OeJZXjUgzFdSOtNtMCJCLvDtWDwfpRVWlyt3y8EvRzI0ccOu9xlMVirawolSCw==", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-9hjRTVoZjRFR6xo8igAJyNXQyPX6Aq++Nhb5ebrUF414dv4jr2MitM2fWiOY475wa3Za7TOS2Gh9fmqEhLTt0A==" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.0.tgz", + "integrity": "sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg==" + }, + "node_modules/@types/d3-polygon": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.0.tgz", + "integrity": "sha512-D49z4DyzTKXM0sGKVqiTDTYr+DHg/uxsiWDAkNrwXYuiZVd9o9wXZIo+YsHkifOiyBkmSWlEngHCQme54/hnHw==" + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.2.tgz", + "integrity": "sha512-QNcK8Jguvc8lU+4OfeNx+qnVy7c0VrDJ+CCVFS9srBo2GL9Y18CnIxBdTF3v38flrGy5s1YggcoAiu6s4fLQIw==" + }, + "node_modules/@types/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-IIE6YTekGczpLYo/HehAy3JGF1ty7+usI97LqraNa8IiDur+L44d0VOjAvFQWJVdZOJHukUJw+ZdZBlgeUsHOQ==" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.3.tgz", + "integrity": "sha512-PATBiMCpvHJSMtZAMEhc2WyL+hnzarKzI6wAHYjhsonjWJYGq5BXTzQjv4l8m2jO183/4wZ90rKvSeT7o72xNQ==", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz", + "integrity": "sha512-dsoJGEIShosKVRBZB0Vo3C8nqSDqVGujJU6tPznsBJxNJNwMF8utmS83nvCBKQYPpjCzaaHcrf66iTRpZosLPw==" + }, + "node_modules/@types/d3-selection": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.5.tgz", + "integrity": "sha512-xCB0z3Hi8eFIqyja3vW8iV01+OHGYR2di/+e+AiOcXIOrY82lcvWW8Ke1DYE/EUVMsBl4Db9RppSBS3X1U6J0w==" + }, + "node_modules/@types/d3-shape": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.1.tgz", + "integrity": "sha512-6Uh86YFF7LGg4PQkuO2oG6EMBRLuW9cbavUW46zkIO5kuS2PfTqo2o9SkgtQzguBHbLgNnU90UNsITpsX1My+A==", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.0.tgz", + "integrity": "sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==" + }, + "node_modules/@types/d3-time-format": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.0.tgz", + "integrity": "sha512-yjfBUe6DJBsDin2BMIulhSHmr5qNR5Pxs17+oW4DoVPyVIXZ+m6bs7j1UVKP08Emv6jRmYrYqxYzO63mQxy1rw==" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.0.tgz", + "integrity": "sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g==" + }, + "node_modules/@types/d3-transition": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.3.tgz", + "integrity": "sha512-/S90Od8Id1wgQNvIA8iFv9jRhCiZcGhPd2qX0bKF/PS+y0W5CrXKgIiELd2CvG1mlQrWK/qlYh3VxicqG1ZvgA==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.3.tgz", + "integrity": "sha512-OWk1yYIIWcZ07+igN6BeoG6rqhnJ/pYe+R1qWFM2DtW49zsoSjgb9G5xB0ZXA8hh2jAzey1XuRmMSoXdKw8MDA==", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, "node_modules/@types/eslint": { "version": "8.44.0", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.0.tgz", @@ -4035,6 +4373,11 @@ "@types/send": "*" } }, + "node_modules/@types/geojson": { + "version": "7946.0.10", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz", + "integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==" + }, "node_modules/@types/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", @@ -5905,6 +6248,11 @@ "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", "dev": true }, + "node_modules/classcat": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.4.tgz", + "integrity": "sha512-sbpkOw6z413p+HDGcBENe498WM9woqWHiJxCq7nvmxe9WmrUmqfAcxpIwAiMtM5Q3AhYkzXcNQHqsWq0mND51g==" + }, "node_modules/clean-css": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz", @@ -5953,6 +6301,15 @@ "wrap-ansi": "^7.0.0" } }, + "node_modules/clsx": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", + "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -6675,6 +7032,102 @@ "type": "^1.0.1" } }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -9457,7 +9910,7 @@ "version": "9.0.21", "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", - "dev": true, + "devOptional": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/immer" @@ -15364,6 +15817,23 @@ } } }, + "node_modules/reactflow": { + "version": "11.7.4", + "resolved": "https://registry.npmjs.org/reactflow/-/reactflow-11.7.4.tgz", + "integrity": "sha512-QI6+oc1Ft6oFeLSdHlp+SmgymbI5Tm49wj5JyE84O4A54yN/ImfYaBhLit9Cmfzxn9Tz6tDqmGMGbk4bdtB8/w==", + "dependencies": { + "@reactflow/background": "11.2.4", + "@reactflow/controls": "11.1.15", + "@reactflow/core": "11.7.4", + "@reactflow/minimap": "11.5.4", + "@reactflow/node-resizer": "2.1.1", + "@reactflow/node-toolbar": "1.2.3" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -17005,6 +17475,12 @@ "next-tick": "1" } }, + "node_modules/tiny-typed-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz", + "integrity": "sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA==", + "dev": true + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -17438,6 +17914,14 @@ "requires-port": "^1.0.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -18499,6 +18983,29 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zustand": { + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.3.9.tgz", + "integrity": "sha512-Tat5r8jOMG1Vcsj8uldMyqYKC5IZvQif8zetmLHs9WoZlntTHmIoNM8TpLRY31ExncuUvUOXehd0kvahkuHjDw==", + "dependencies": { + "use-sync-external-store": "1.2.0" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "immer": ">=9.0", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } } } } diff --git a/crates/compiler/checkmate/www/package.json b/crates/compiler/checkmate/www/package.json index 3dfb8eac025..538492a8ed4 100644 --- a/crates/compiler/checkmate/www/package.json +++ b/crates/compiler/checkmate/www/package.json @@ -3,8 +3,10 @@ "version": "0.1.0", "private": true, "dependencies": { + "@dagrejs/dagre": "^1.0.2", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "reactflow": "^11.7.4" }, "scripts": { "start": "react-scripts start", @@ -37,9 +39,11 @@ "@types/node": "^16.18.38", "@types/react": "^18.2.15", "@types/react-dom": "^18.2.7", + "clsx": "^2.0.0", "json-schema-to-typescript": "^13.0.2", "react-scripts": "5.0.1", "tailwindcss": "^3.3.3", + "tiny-typed-emitter": "^2.1.0", "typescript": "^4.9.5" } } diff --git a/crates/compiler/checkmate/www/src/App.tsx b/crates/compiler/checkmate/www/src/App.tsx index f275bb4634c..ad3dac2ffb8 100644 --- a/crates/compiler/checkmate/www/src/App.tsx +++ b/crates/compiler/checkmate/www/src/App.tsx @@ -1,13 +1,39 @@ import React from "react"; +import FileInput, { LoadedEvents } from "./components/FileInput"; +import Ui from "./components/Ui"; +import data from "./checkmate.json"; +import { AllEvents } from "./schema"; export default function App() { + const [events, setEvents] = React.useState({ + kind: "ok", + events: data as AllEvents, + }); + return ( -
-
-

- Edit src/App.tsx and save to reload. -

-
+
+
+ +
+
+ +
); } + +interface EventsWrapperProps { + events: LoadedEvents | null; +} + +function EventsWrapper({ events }: EventsWrapperProps): JSX.Element { + if (events === null) { + return
; + } + switch (events.kind) { + case "ok": + return ; + case "err": + return
{events.error}
; + } +} diff --git a/crates/compiler/checkmate/www/src/checkmate.json b/crates/compiler/checkmate/www/src/checkmate.json new file mode 100644 index 00000000000..fc7d47d91a4 --- /dev/null +++ b/crates/compiler/checkmate/www/src/checkmate.json @@ -0,0 +1,545 @@ +[ + { + "type": "Unification", + "left": 1540, + "right": 71, + "mode": { "type": "Eq" }, + "success": true, + "subevents": [ + { + "type": "VariableSetDescriptor", + "variable": 71, + "rank": 1, + "content": { + "type": "Function", + "arguments": [1543, 1544], + "lambda_type": 1542, + "ret": 1541 + } + }, + { "type": "VariableUnified", "from": 1540, "to": 71 }, + { "type": "VariableUnified", "from": 71, "to": 71 } + ] + }, + { + "type": "Unification", + "left": 71, + "right": 1546, + "mode": { "type": "Eq" }, + "success": true, + "subevents": [ + { + "type": "Unification", + "left": 1543, + "right": 67, + "mode": { "type": "Eq" }, + "success": true, + "subevents": [ + { + "type": "VariableSetDescriptor", + "variable": 67, + "rank": 1, + "content": { + "type": "Alias", + "name": "`Num.Num`", + "variables": { + "type_variables": [1545], + "lambda_set_variables": [], + "infer_ext_in_output_position_variables": [] + }, + "real_variable": 1545, + "kind": { "type": "Opaque" } + } + }, + { "type": "VariableUnified", "from": 1543, "to": 67 }, + { "type": "VariableUnified", "from": 67, "to": 67 } + ] + }, + { + "type": "Unification", + "left": 1544, + "right": 69, + "mode": { "type": "Eq" }, + "success": true, + "subevents": [ + { + "type": "VariableSetDescriptor", + "variable": 69, + "rank": 1, + "content": { + "type": "Alias", + "name": "`Num.Num`", + "variables": { + "type_variables": [1545], + "lambda_set_variables": [], + "infer_ext_in_output_position_variables": [] + }, + "real_variable": 1545, + "kind": { "type": "Opaque" } + } + }, + { "type": "VariableUnified", "from": 1544, "to": 69 }, + { "type": "VariableUnified", "from": 69, "to": 69 } + ] + }, + { + "type": "Unification", + "left": 1541, + "right": 73, + "mode": { "type": "Eq" }, + "success": true, + "subevents": [ + { + "type": "VariableSetDescriptor", + "variable": 73, + "rank": 1, + "content": { + "type": "Alias", + "name": "`Num.Num`", + "variables": { + "type_variables": [1545], + "lambda_set_variables": [], + "infer_ext_in_output_position_variables": [] + }, + "real_variable": 1545, + "kind": { "type": "Opaque" } + } + }, + { "type": "VariableUnified", "from": 1541, "to": 73 }, + { "type": "VariableUnified", "from": 73, "to": 73 } + ] + }, + { + "type": "Unification", + "left": 1542, + "right": 72, + "mode": { "type": "Eq" }, + "success": true, + "subevents": [ + { + "type": "VariableSetDescriptor", + "variable": 72, + "rank": 1, + "content": { + "type": "LambdaSet", + "solved": [{ "function": "`Num.add`", "environment": [] }], + "unspecialized": [], + "recursion_var": null, + "ambient_function": 1540 + } + }, + { "type": "VariableUnified", "from": 1542, "to": 72 }, + { "type": "VariableUnified", "from": 72, "to": 72 } + ] + }, + { + "type": "VariableSetDescriptor", + "variable": 1546, + "rank": 1, + "content": { + "type": "Function", + "arguments": [67, 69], + "lambda_type": 1542, + "ret": 73 + } + }, + { "type": "VariableUnified", "from": 71, "to": 1546 }, + { "type": "VariableUnified", "from": 1546, "to": 1546 } + ] + }, + { + "type": "Unification", + "left": 66, + "right": 1547, + "mode": { "type": "Eq" }, + "success": true, + "subevents": [ + { + "type": "VariableSetDescriptor", + "variable": 1547, + "rank": 1, + "content": { + "type": "RangedNumber", + "range": { + "kind": { "type": "AnyNum" }, + "signed": true, + "min_width": 8 + } + } + }, + { "type": "VariableUnified", "from": 66, "to": 1547 }, + { "type": "VariableUnified", "from": 1547, "to": 1547 } + ] + }, + { + "type": "Unification", + "left": 1548, + "right": 67, + "mode": { "type": "Eq" }, + "success": true, + "subevents": [ + { + "type": "Unification", + "left": 66, + "right": 1545, + "mode": { "type": "Eq" }, + "success": true, + "subevents": [ + { + "type": "VariableSetDescriptor", + "variable": 1545, + "rank": 1, + "content": { + "type": "RangedNumber", + "range": { + "kind": { "type": "AnyNum" }, + "signed": true, + "min_width": 8 + } + } + }, + { "type": "VariableUnified", "from": 1547, "to": 1545 }, + { "type": "VariableUnified", "from": 1545, "to": 1545 } + ] + }, + { + "type": "VariableSetDescriptor", + "variable": 67, + "rank": 1, + "content": { + "type": "Alias", + "name": "`Num.Num`", + "variables": { + "type_variables": [66], + "lambda_set_variables": [], + "infer_ext_in_output_position_variables": [] + }, + "real_variable": 66, + "kind": { "type": "Opaque" } + } + }, + { "type": "VariableUnified", "from": 1548, "to": 67 }, + { "type": "VariableUnified", "from": 67, "to": 67 } + ] + }, + { + "type": "Unification", + "left": 68, + "right": 1549, + "mode": { "type": "Eq" }, + "success": true, + "subevents": [ + { + "type": "VariableSetDescriptor", + "variable": 1549, + "rank": 1, + "content": { + "type": "RangedNumber", + "range": { + "kind": { "type": "AnyNum" }, + "signed": true, + "min_width": 8 + } + } + }, + { "type": "VariableUnified", "from": 68, "to": 1549 }, + { "type": "VariableUnified", "from": 1549, "to": 1549 } + ] + }, + { + "type": "Unification", + "left": 1550, + "right": 69, + "mode": { "type": "Eq" }, + "success": true, + "subevents": [ + { + "type": "Unification", + "left": 68, + "right": 1545, + "mode": { "type": "Eq" }, + "success": true, + "subevents": [ + { + "type": "VariableSetDescriptor", + "variable": 1545, + "rank": 1, + "content": { + "type": "RangedNumber", + "range": { + "kind": { "type": "AnyNum" }, + "signed": true, + "min_width": 8 + } + } + }, + { "type": "VariableUnified", "from": 1549, "to": 1545 }, + { "type": "VariableUnified", "from": 1545, "to": 1545 } + ] + }, + { + "type": "VariableSetDescriptor", + "variable": 69, + "rank": 1, + "content": { + "type": "Alias", + "name": "`Num.Num`", + "variables": { + "type_variables": [68], + "lambda_set_variables": [], + "infer_ext_in_output_position_variables": [] + }, + "real_variable": 68, + "kind": { "type": "Opaque" } + } + }, + { "type": "VariableUnified", "from": 1550, "to": 69 }, + { "type": "VariableUnified", "from": 69, "to": 69 } + ] + }, + { + "type": "Unification", + "left": 73, + "right": 1551, + "mode": { "type": "Eq" }, + "success": true, + "subevents": [ + { + "type": "Unification", + "left": 73, + "right": 1552, + "mode": { "type": "Eq" }, + "success": true, + "subevents": [ + { + "type": "Unification", + "left": 1545, + "right": 1553, + "mode": { "type": "Eq" }, + "success": true, + "subevents": [ + { + "type": "Unification", + "left": 1556, + "right": 1554, + "mode": { "type": "Eq" }, + "success": true, + "subevents": [ + { + "type": "VariableSetDescriptor", + "variable": 1554, + "rank": 1, + "content": { + "type": "Alias", + "name": "`Num.Unsigned8`", + "variables": { + "type_variables": [], + "lambda_set_variables": [], + "infer_ext_in_output_position_variables": [] + }, + "real_variable": 1555, + "kind": { "type": "Opaque" } + } + }, + { "type": "VariableUnified", "from": 1556, "to": 1554 }, + { "type": "VariableUnified", "from": 1554, "to": 1554 } + ] + } + ] + }, + { + "type": "Unification", + "left": 1545, + "right": 1553, + "mode": { "type": "Eq" }, + "success": true, + "subevents": [ + { + "type": "VariableSetDescriptor", + "variable": 1553, + "rank": 1, + "content": { + "type": "Alias", + "name": "`Num.Integer`", + "variables": { + "type_variables": [1556], + "lambda_set_variables": [], + "infer_ext_in_output_position_variables": [] + }, + "real_variable": 1556, + "kind": { "type": "Opaque" } + } + }, + { "type": "VariableUnified", "from": 1545, "to": 1553 }, + { "type": "VariableUnified", "from": 1553, "to": 1553 } + ] + }, + { + "type": "VariableSetDescriptor", + "variable": 1552, + "rank": 1, + "content": { + "type": "Alias", + "name": "`Num.Num`", + "variables": { + "type_variables": [1545], + "lambda_set_variables": [], + "infer_ext_in_output_position_variables": [] + }, + "real_variable": 1545, + "kind": { "type": "Opaque" } + } + }, + { "type": "VariableUnified", "from": 73, "to": 1552 }, + { "type": "VariableUnified", "from": 1552, "to": 1552 } + ] + } + ] + }, + { + "type": "Unification", + "left": 1551, + "right": 75, + "mode": { "type": "Eq" }, + "success": true, + "subevents": [ + { + "type": "VariableSetDescriptor", + "variable": 75, + "rank": 1, + "content": { + "type": "Alias", + "name": "`Num.U8`", + "variables": { + "type_variables": [], + "lambda_set_variables": [], + "infer_ext_in_output_position_variables": [] + }, + "real_variable": 1552, + "kind": { "type": "Structural" } + } + }, + { "type": "VariableUnified", "from": 1551, "to": 75 }, + { "type": "VariableUnified", "from": 75, "to": 75 } + ] + }, + { + "type": "Unification", + "left": 80, + "right": 75, + "mode": { "type": "Eq" }, + "success": true, + "subevents": [ + { + "type": "VariableSetDescriptor", + "variable": 75, + "rank": 1, + "content": { + "type": "Alias", + "name": "`Num.U8`", + "variables": { + "type_variables": [], + "lambda_set_variables": [], + "infer_ext_in_output_position_variables": [] + }, + "real_variable": 1552, + "kind": { "type": "Structural" } + } + }, + { "type": "VariableUnified", "from": 80, "to": 75 }, + { "type": "VariableUnified", "from": 75, "to": 75 } + ] + }, + { + "type": "Unification", + "left": 1557, + "right": 79, + "mode": { "type": "Eq" }, + "success": true, + "subevents": [ + { + "type": "VariableSetDescriptor", + "variable": 79, + "rank": 1, + "content": { + "type": "Alias", + "name": "`Bool.Bool`", + "variables": { + "type_variables": [], + "lambda_set_variables": [], + "infer_ext_in_output_position_variables": [] + }, + "real_variable": 1558, + "kind": { "type": "Opaque" } + } + }, + { "type": "VariableUnified", "from": 1557, "to": 79 }, + { "type": "VariableUnified", "from": 79, "to": 79 } + ] + }, + { + "type": "Unification", + "left": 79, + "right": 5, + "mode": { "type": "Eq" }, + "success": true, + "subevents": [ + { + "type": "Unification", + "left": 1558, + "right": 4, + "mode": { "type": "Eq" }, + "success": true, + "subevents": [ + { + "type": "Unification", + "left": 84, + "right": 3, + "mode": { "type": "Eq" }, + "success": true, + "subevents": [ + { + "type": "VariableSetDescriptor", + "variable": 3, + "rank": 0, + "content": { "type": "EmptyTagUnion" } + }, + { "type": "VariableUnified", "from": 84, "to": 3 }, + { "type": "VariableUnified", "from": 3, "to": 3 } + ] + }, + { + "type": "VariableSetDescriptor", + "variable": 4, + "rank": 0, + "content": { + "type": "TagUnion", + "tags": { "False": [], "True": [] }, + "extension": { "type": "Any", "variable": 84 } + } + }, + { "type": "VariableUnified", "from": 1558, "to": 4 }, + { "type": "VariableUnified", "from": 4, "to": 4 } + ] + }, + { + "type": "VariableSetDescriptor", + "variable": 5, + "rank": 0, + "content": { + "type": "Alias", + "name": "`Bool.Bool`", + "variables": { + "type_variables": [], + "lambda_set_variables": [], + "infer_ext_in_output_position_variables": [] + }, + "real_variable": 1558, + "kind": { "type": "Opaque" } + } + }, + { "type": "VariableUnified", "from": 79, "to": 5 }, + { "type": "VariableUnified", "from": 5, "to": 5 } + ] + } +] diff --git a/crates/compiler/checkmate/www/src/components/Common/Variable.tsx b/crates/compiler/checkmate/www/src/components/Common/Variable.tsx new file mode 100644 index 00000000000..d0f8545b6c4 --- /dev/null +++ b/crates/compiler/checkmate/www/src/components/Common/Variable.tsx @@ -0,0 +1,83 @@ +import { ComponentProps } from "react"; +import clsx from "clsx"; +import { QuerySubs, TypeDescriptor } from "../../engine/subs"; +import { Variable } from "../../schema"; +import DrawHeadConstructor from "../Content/HeadConstructor"; +import { contentStyles } from "./../Content"; + +interface VariableElProps { + variable: Variable; + subs: QuerySubs; + onClick?: (variable: Variable) => void; + nested?: boolean; + raw?: boolean; +} + +export function VariableElPretty(props: VariableElProps): JSX.Element { + const { variable, subs } = props; + const desc = subs.get_root(variable); + const content = ( + ( + + )} + drawVariableRaw={(variable) => ( + + )} + /> + ); + return ( + + {content} + + ); +} + +function VariableElRaw(props: VariableElProps): JSX.Element { + const desc = props.subs.get_root(props.variable); + return ; +} + +function Helper({ + children, + variable, + desc, + onClick, + nested, + raw, +}: VariableElProps & + Pick, "children"> & { + desc: TypeDescriptor | undefined; + }): JSX.Element { + const { bg } = contentStyles(desc); + const varHeader = + !nested || raw ? ( + { + e.stopPropagation(); + onClick?.(variable); + }} + > + {variable} + + ) : ( + <> + ); + return ( + + {varHeader} + {children ? {children} : <>} + + ); +} diff --git a/crates/compiler/checkmate/www/src/components/Content/HeadConstructor.tsx b/crates/compiler/checkmate/www/src/components/Content/HeadConstructor.tsx new file mode 100644 index 00000000000..3d3f1af0be8 --- /dev/null +++ b/crates/compiler/checkmate/www/src/components/Content/HeadConstructor.tsx @@ -0,0 +1,344 @@ +import { TypeDescriptor } from "../../engine/subs"; +import { + ClosureType, + RecordFieldKind, + UnspecializedClosureType, + Variable, +} from "../../schema"; + +type DrawVariable = (variable: Variable) => JSX.Element; + +export interface DrawHeadConstructorProps { + desc: TypeDescriptor | undefined; + drawVariablePretty: DrawVariable; + drawVariableRaw: DrawVariable; +} + +export default function DrawHeadConstructor({ + desc, + drawVariablePretty, + drawVariableRaw, +}: DrawHeadConstructorProps): JSX.Element { + if (!desc) { + return <>???; + } + const content = desc.content; + switch (content.type) { + case "Flex": + case "Rigid": { + const { name } = content; + return name ? <>{name} : <>_; + } + case "FlexAble": + case "RigidAble": { + const { name, abilities } = content; + const nameEl = name ? <>{name} : <>_; + return ( + <> + {nameEl} has {abilities.join(", ")} + + ); + } + case "Recursive": { + const { name, structure } = content; + const structureEl = drawVariableRaw(structure); + const nameEl = name ? <>{name} to : <>; + return ( + <> + <{nameEl} + {structureEl}> + + ); + } + case "LambdaSet": { + const { ambient_function, solved, unspecialized, recursion_var } = + content; + const ambientFunctionEl = drawVariableRaw(ambient_function); + const solvedEl = ( + + ); + const unspecializedEl = ( + + ); + const recursionVarEl = recursion_var ? ( + <> as <{drawVariableRaw(recursion_var)}> + ) : ( + <> + ); + return ( + <> + [{solvedEl} + {unspecializedEl}]{recursionVarEl} ^{ambientFunctionEl} + + ); + } + case "ErasedLambda": { + return <>?; + } + case "Alias": { + const { kind, name, variables } = content; + const prefix = kind.type === "Opaque" ? "@" : ""; + const variablesEl = ( + + ); + return ( + + {prefix} + {sym(name)} + {variablesEl} + + ); + } + case "Apply": { + const { symbol, variables } = content; + const variablesEl = ( + + ); + return ( + <> + {sym(symbol)} + {variablesEl} + + ); + } + case "Function": { + const { arguments: args, lambda_type, ret } = content; + const argsEl = args.map((arg, i) => ( + + {i !== 0 ? ", " : ""} + {drawVariablePretty(arg)} + + )); + const lambdaTypeEl = drawVariablePretty(lambda_type); + const retEl = drawVariablePretty(ret); + return ( + <> + {argsEl} + {" -"} + {lambdaTypeEl} + {"-> "} + {retEl} + + ); + } + case "Record": { + const { fields, extension } = content; + const fieldsEl = Object.entries(fields).map(([key, value], i) => { + const { field_type, kind } = value; + return ( + + {i !== 0 ? ", " : ""} + {key} {}{" "} + {drawVariablePretty(field_type)} + + ); + }); + return ( + <> + {"{"} + {fieldsEl} + {"}"} + {drawVariablePretty(extension)} + + ); + } + case "Tuple": { + const { elements, extension } = content; + const elemsEl = Object.entries(elements).map(([key, value], i) => { + return ( + + {i !== 0 ? ", " : ""} + {key}: {drawVariablePretty(value)} + + ); + }); + return ( + <> + ({elemsEl}){drawVariablePretty(extension)} + + ); + } + case "TagUnion": { + const { tags, extension } = content; + return ( + <> + + {drawVariablePretty(extension.variable)} + + ); + } + case "RecursiveTagUnion": { + const { tags, extension, recursion_var } = content; + return ( + <> + ( + {drawVariablePretty(extension.variable)} as < + {drawVariableRaw(recursion_var)}>) + + ); + } + case "FunctionOrTagUnion": { + const { functions, tags, extension } = content; + const functionsEl = functions.map((f, i) => ( + + {i !== 0 ? ", " : ""} + {sym(f)} + + )); + const tagsEl = tags.map((t, i) => ( + + {i !== 0 ? ", " : ""} + {t} + + )); + return ( + <> + [{functionsEl} | {tagsEl}]{drawVariablePretty(extension.variable)} + + ); + } + case "RangedNumber": { + const { + range: { kind, min_width, signed }, + } = content; + switch (kind.type) { + case "AnyNum": + return <>ℚ{min_width}+; + case "Int": + return signed ? <>ℤ{min_width}+ : <>ℕ{min_width}+; + } + break; + } + case "EmptyRecord": { + return <>{"{}"}; + } + case "EmptyTuple": { + return <>(); + } + case "EmptyTagUnion": { + return <>[]; + } + case "Error": { + return <>⊥; + } + } +} + +function DrawVarArgumentsList({ + variables, + drawVariableRaw, +}: { + variables: Variable[]; + drawVariableRaw: DrawVariable; +}): JSX.Element { + return variables.length !== 0 ? ( + <> + {" "} + {variables.map((v, i) => ( + {drawVariableRaw(v)} + ))} + + ) : ( + <> + ); +} + +function DrawSolved({ + solved, + drawVariableRaw, +}: { + solved: ClosureType[]; + drawVariableRaw: DrawVariable; +}): JSX.Element { + const tags = solved.map(({ environment, function: fn }, i) => ( + + {i !== 0 ? ", " : ""} + + + )); + return <>[{tags}]; +} + +function DrawTags({ + tags, + drawVariableRaw, +}: { + tags: Record; + drawVariableRaw: DrawVariable; +}): JSX.Element { + const tagsEl = Object.entries(tags).map(([tag, vars], i) => ( + + {i !== 0 ? ", " : ""} + + + )); + return <>[{tagsEl}]; +} + +function DrawUnspecialized({ + unspecialized, + drawVariableRaw, +}: { + unspecialized: UnspecializedClosureType[]; + drawVariableRaw: DrawVariable; +}): JSX.Element { + const unspecs = unspecialized.map( + ({ ability_member, lambda_set_region, specialization }, i) => ( + + {" + "} + {drawVariableRaw(specialization)}:{sym(ability_member)}: + {lambda_set_region} + + ) + ); + return <>{unspecs}; +} + +function DrawTag({ + tag, + variables, + drawVariableRaw, +}: { + tag: string; + variables: Variable[]; + drawVariableRaw: DrawVariable; +}): JSX.Element { + return ( + <> + {tag} + + + ); +} + +function DrawFieldKind({ kind }: { kind: RecordFieldKind }): JSX.Element { + switch (kind.type) { + case "Required": + case "Demanded": + return <>:; + case "Optional": + return <>?; + } +} + +function sym(symbol: string): string { + if (symbol.startsWith("`")) symbol = symbol.slice(1); + if (symbol.endsWith("`")) symbol = symbol.slice(0, -1); + return symbol.split(".").at(-1)!; +} diff --git a/crates/compiler/checkmate/www/src/components/Content/index.ts b/crates/compiler/checkmate/www/src/components/Content/index.ts new file mode 100644 index 00000000000..1fdce9fb980 --- /dev/null +++ b/crates/compiler/checkmate/www/src/components/Content/index.ts @@ -0,0 +1,66 @@ +import { TypeDescriptor } from "../../engine/subs"; +import { assertExhaustive } from "../../utils/exhaustive"; + +export interface ContentStyles { + name: string; + bg: string; +} + +export function contentStyles(desc: TypeDescriptor | undefined): ContentStyles { + if (!desc) { + return { name: "???", bg: "bg-red-500" }; + } + + const content = desc.content; + switch (content.type) { + case "Flex": + return { name: "Flex", bg: "bg-blue-300" }; + case "FlexAble": + return { name: "FlexAble", bg: "bg-blue-400" }; + case "Rigid": + return { name: "Rigid", bg: "bg-indigo-300" }; + case "RigidAble": + return { name: "RigidAble", bg: "bg-indigo-400" }; + case "Recursive": + return { name: "Rec", bg: "bg-blue-grey-500" }; + case "LambdaSet": + return { name: "LambdaSet", bg: "bg-green-500" }; + case "ErasedLambda": + return { name: "ErasedLambda", bg: "bg-green-700" }; + case "Alias": { + switch (content.kind.type) { + case "Structural": + return { name: "Alias", bg: "bg-yellow-300" }; + case "Opaque": + return { name: "Opaque", bg: "bg-amber-400" }; + default: + assertExhaustive(content.kind); + } + break; + } + case "Apply": + return { name: "Apply", bg: "bg-orange-500" }; + case "Function": + return { name: "Func", bg: "bg-teal-400" }; + case "Record": + return { name: "Record", bg: "bg-purple-400" }; + case "Tuple": + return { name: "Tuple", bg: "bg-deep-purple-400" }; + case "TagUnion": + return { name: "Tags", bg: "bg-cyan-200" }; + case "FunctionOrTagUnion": + return { name: "Func|Tags", bg: "bg-cyan-300" }; + case "RecursiveTagUnion": + return { name: "RecTags", bg: "bg-cyan-400" }; + case "RangedNumber": + return { name: "ℕ", bg: "bg-lime-400" }; + case "EmptyRecord": + return { name: "{}", bg: "bg-purple-400" }; + case "EmptyTuple": + return { name: "()", bg: "bg-deep-purple-400" }; + case "EmptyTagUnion": + return { name: "[]", bg: "bg-cyan-200" }; + case "Error": + return { name: "Error", bg: "bg-red-400" }; + } +} diff --git a/crates/compiler/checkmate/www/src/components/EventItem/Variable.tsx b/crates/compiler/checkmate/www/src/components/EventItem/Variable.tsx new file mode 100644 index 00000000000..1bcd88de6b5 --- /dev/null +++ b/crates/compiler/checkmate/www/src/components/EventItem/Variable.tsx @@ -0,0 +1,27 @@ +import { EventIndex } from "../../engine/engine"; +import { Variable } from "../../schema"; +import { VariableElPretty } from "../Common/Variable"; +import { CommonProps } from "./types"; + +interface VariableProps extends CommonProps { + index: EventIndex; + variable: Variable; +} + +export function VariableEl({ + engine, + toggleVariableVis, + index, + variable, +}: VariableProps): JSX.Element { + engine.stepTo(index); + return ( + { + toggleVariableVis(variable); + }} + > + ); +} diff --git a/crates/compiler/checkmate/www/src/components/EventItem/types.ts b/crates/compiler/checkmate/www/src/components/EventItem/types.ts new file mode 100644 index 00000000000..c0512edf225 --- /dev/null +++ b/crates/compiler/checkmate/www/src/components/EventItem/types.ts @@ -0,0 +1,7 @@ +import type { Engine } from "../../engine/engine"; +import type { Variable } from "../../schema"; + +export interface CommonProps { + engine: Engine; + toggleVariableVis: (variable: Variable) => void; +} diff --git a/crates/compiler/checkmate/www/src/components/EventList.tsx b/crates/compiler/checkmate/www/src/components/EventList.tsx new file mode 100644 index 00000000000..818670c42ac --- /dev/null +++ b/crates/compiler/checkmate/www/src/components/EventList.tsx @@ -0,0 +1,145 @@ +import clsx from "clsx"; +import React from "react"; +import { EventIndex } from "../engine/engine"; +import { lastSubEvent } from "../engine/event_util"; +import { UnificationMode, Event } from "../schema"; +import { Refine } from "../utils/refine"; +import { CommonProps } from "./EventItem/types"; +import { VariableEl } from "./EventItem/Variable"; + +interface EventListProps extends CommonProps { + events: Event[]; + root?: boolean; +} + +const MT = "mt-2.5"; +const UNFOCUSED = "opacity-40"; + +export default function EventList(props: EventListProps): JSX.Element { + const { events, root } = props; + return ( +
    + {events.map((event, i) => ( +
  • + +
  • + ))} +
+ ); +} + +interface OneEventProps extends CommonProps { + event: Event; +} + +function OneEvent(props: OneEventProps): JSX.Element { + const { event } = props; + switch (event.type) { + case "Unification": + return ; + case "VariableUnified": + return <>; + case "VariableSetDescriptor": + return <>; + } +} + +const DROPDOWN_CLOSED = "▶"; +const DROPDOWN_OPEN = "▼"; + +const UN_UNKNOWN = "💭"; +const UN_SUCCESS = "✅"; +const UN_FAILURE = "❌"; + +interface UnificationProps extends CommonProps { + event: Refine; +} + +function Unification(props: UnificationProps): JSX.Element { + const { engine, event } = props; + const { mode, subevents, success } = event; + + const beforeUnificationIndex = engine.getEventIndex(event); + const afterUnificationIndex = engine.getEventIndex(lastSubEvent(event)); + + const leftVar = (index: EventIndex) => ( + + ); + const rightVar = (index: EventIndex) => ( + + ); + + const [isOpen, setIsOpen] = React.useState(false); + + const modeIcon = ; + + const resultIcon = success ? UN_SUCCESS : UN_FAILURE; + const resultHeadline = ; + const topHeadline = ( + + ); + + function getHeadline(index: EventIndex) { + return ( + + ); + } + + if (!isOpen) { + const headLine = getHeadline(afterUnificationIndex); + return
{headLine}
; + } else { + const dropdownTransparent = ( + {DROPDOWN_OPEN} + ); + + const headlineBefore = getHeadline(beforeUnificationIndex); + + const headlineAfter = ( +
+ {dropdownTransparent} + {resultHeadline} {leftVar(afterUnificationIndex)} {modeIcon}{" "} + {rightVar(afterUnificationIndex)} +
+ ); + + return ( +
+
{headlineBefore}
+ + {headlineAfter} +
+ ); + } +} + +function Headline({ icon }: { icon: string }): JSX.Element { + return {icon}; +} + +function UnificationModeIcon({ mode }: { mode: UnificationMode }): JSX.Element { + switch (mode.type) { + case "Eq": + return <>~; + case "Present": + return <>+=; + case "LambdaSetSpecialization": + return <>|~|; + } +} diff --git a/crates/compiler/checkmate/www/src/components/Events/index.ts b/crates/compiler/checkmate/www/src/components/Events/index.ts new file mode 100644 index 00000000000..6382c113066 --- /dev/null +++ b/crates/compiler/checkmate/www/src/components/Events/index.ts @@ -0,0 +1,4 @@ +import { Variable } from "../../schema"; + +export type ToggleVariableHandler = (variable: Variable) => void; +export type KeydownHandler = (key: string) => void; diff --git a/crates/compiler/checkmate/www/src/components/FileInput.tsx b/crates/compiler/checkmate/www/src/components/FileInput.tsx new file mode 100644 index 00000000000..40e9efbd23f --- /dev/null +++ b/crates/compiler/checkmate/www/src/components/FileInput.tsx @@ -0,0 +1,48 @@ +import { AllEvents } from "../schema"; + +export type EventsOk = { + kind: "ok"; + events: AllEvents; +}; + +export type EventsErr = { + kind: "err"; + error: string; +}; + +export type LoadedEvents = EventsOk | EventsErr; + +interface FileInputProps { + setResult(result: LoadedEvents): void; +} + +export default function FileInput({ setResult }: FileInputProps) { + async function setFile(e: React.ChangeEvent) { + e.preventDefault(); + const files = e.target.files; + if (!files) { + setResult({ kind: "err", error: "Please choose a checkmate file." }); + return; + } + const file = files[0]; + const buf = await file.text(); + try { + const events: AllEvents = JSON.parse(buf); + setResult({ kind: "ok", events }); + } catch (e) { + setResult({ kind: "err", error: "Invalid checkmate file." }); + return; + } + } + + return ( + setFile(e)} + className="block w-full border border-gray-200 shadow-sm rounded-md text-sm + file:bg-roc-purple-bg file:border-0 file:mr-4 file:py-2 file:px-4 cursor-pointer" + > + ); +} diff --git a/crates/compiler/checkmate/www/src/components/Graph/VariableNode.tsx b/crates/compiler/checkmate/www/src/components/Graph/VariableNode.tsx new file mode 100644 index 00000000000..03453f5a786 --- /dev/null +++ b/crates/compiler/checkmate/www/src/components/Graph/VariableNode.tsx @@ -0,0 +1,195 @@ +import clsx from "clsx"; +import { Handle, Position } from "reactflow"; +import { Variable } from "../../schema"; +import { assertExhaustive } from "../../utils/exhaustive"; +import { contentStyles } from "../Content"; +import { VariableElPretty } from "../Common/Variable"; +import { SubsSnapshot, TypeDescriptor } from "../../engine/subs"; + +type AddSubVariableLink = (from: Variable, subVariable: Variable) => void; + +export interface VariableNodeProps { + data: { + subs: SubsSnapshot; + variable: Variable; + addSubVariableLink: AddSubVariableLink; + }; +} + +export default function VariableNode({ data }: VariableNodeProps): JSX.Element { + const { variable, subs, addSubVariableLink } = data; + + const desc = subs.get_root(variable); + const styles = contentStyles(desc); + const basis: BasisProps = { + subs, + origin: variable, + addSubVariableLink, + }; + + const content = Object.entries( + VariableNodeContent(variable, desc, basis) + ).filter((el): el is [string, JSX.Element] => !!el[1]); + + let expandedContent = <>; + if (content.length > 0) { + expandedContent = ( +
    + {content.map(([key, value], i) => ( +
  • + {key}: {value} +
  • + ))} +
+ ); + } + + return ( +
+ +
+ +
+ {expandedContent} + +
+ ); +} + +function VariableNodeContent( + variable: Variable, + desc: TypeDescriptor | undefined, + basis: BasisProps +): Record { + if (!desc) return {}; + const { content } = desc; + + switch (content.type) { + case "Flex": + case "Rigid": { + const { name } = content; + return { name: name ? <>{name} : null }; + } + case "FlexAble": + case "RigidAble": { + const { name, abilities } = content; + return { + name: <>{name}, + abilities: <>[{abilities.join(", ")}], + }; + } + case "Recursive": { + const { name, structure } = content; + return { + name: <>{name}, + structure: , + }; + } + case "LambdaSet": { + const { ambient_function, solved, unspecialized, recursion_var } = + content; + return { + "^": , + as: recursion_var ? ( + + ) : null, + }; + } + case "ErasedLambda": { + return {}; + } + case "Alias": { + const { name, real_variable, variables } = content; + return { + name: <>{name}, + }; + } + case "Apply": { + const { name, variables } = content; + return { + name: <>{name}, + }; + } + case "Function": { + const { arguments: args, lambda_type, ret } = content; + return { + args: ( + <> + {args.map((arg, i) => ( + + ))} + + ), + "||": , + ret: , + }; + } + case "FunctionOrTagUnion": { + const { tags, functions, extension } = content; + return { + tags: <>[{tags.join(", ")}], + fns: <>[{functions.join(", ")}], + }; + } + case "TagUnion": { + const { tags, extension } = content; + return {}; + } + case "RecursiveTagUnion": { + const { recursion_var, extension, tags } = content; + return { + as: , + }; + } + case "Record": { + const { fields, extension } = content; + return {}; + } + case "Tuple": { + const { elements, extension } = content; + return {}; + } + case "RangedNumber": { + const { range } = content; + return {}; + } + case "EmptyRecord": + case "EmptyTuple": + case "EmptyTagUnion": + case "Error": { + return {}; + } + default: { + return assertExhaustive(content); + } + } +} + +interface BasisProps { + subs: SubsSnapshot; + origin: Variable; + addSubVariableLink: AddSubVariableLink; +} + +function SubVariable({ + subs, + origin, + variable, + addSubVariableLink, +}: { + variable: Variable; +} & BasisProps): JSX.Element { + return ( + addSubVariableLink(origin, variable)} + /> + ); +} diff --git a/crates/compiler/checkmate/www/src/components/Graph/VariablesGraph.tsx b/crates/compiler/checkmate/www/src/components/Graph/VariablesGraph.tsx new file mode 100644 index 00000000000..9aeb834026f --- /dev/null +++ b/crates/compiler/checkmate/www/src/components/Graph/VariablesGraph.tsx @@ -0,0 +1,312 @@ +import Dagre from "@dagrejs/dagre"; +import ReactFlow, { + Node, + Edge, + Background, + BackgroundVariant, + useReactFlow, + ReactFlowProvider, + NodeChange, + applyNodeChanges, + EdgeChange, + applyEdgeChanges, + Panel, + NodeTypes, +} from "reactflow"; +import { useCallback, useState } from "react"; +import { Variable } from "../../schema"; + +import "reactflow/dist/style.css"; +import clsx from "clsx"; +import VariableNode, { VariableNodeProps } from "./VariableNode"; +import { SubsSnapshot } from "../../engine/subs"; +import { KeydownHandler } from "../Events"; + +export interface VariablesGraphProps { + subs: SubsSnapshot; + onVariable: (handler: (variable: Variable) => void) => void; + onKeydown: (handler: KeydownHandler) => void; +} + +type GraphDirection = "TB" | "BT" | "LR" | "RL"; + +const DEFAULT_DIRECTION: GraphDirection = "TB"; + +interface LayoutedElements { + nodes: Node[]; + edges: Edge[]; +} + +interface ComputeLayoutedElementsProps extends LayoutedElements { + direction: GraphDirection; +} + +function computeLayoutedElements({ + nodes, + edges, + direction, +}: ComputeLayoutedElementsProps): LayoutedElements { + if (nodes.length === 0) { + return { + nodes: [], + edges: [], + }; + } + + const g = new Dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({})); + g.setGraph({ rankdir: direction }); + + edges.forEach((edge) => g.setEdge(edge.source, edge.target)); + nodes.forEach((node) => g.setNode(node.id, node)); + + Dagre.layout(g); + + const result = { + nodes: nodes.map((node) => { + const { x, y } = g.node(node.id); + + return { ...node, position: { x, y } }; + }), + edges, + }; + return result; +} + +const NODE_TYPES: NodeTypes = { + variable: VariableNode, +}; + +function newVariable(id: string, data: VariableNodeProps["data"]): Node { + return { + id, + position: { x: 0, y: 0 }, + type: "variable", + data, + }; +} + +function addNodeChange(node: Node, existingNodes: Node[]): NodeChange | null { + if (existingNodes.some((n) => n.id === node.id)) { + return null; + } + return { + type: "add", + item: node, + }; +} + +function addEdgeChange(edge: Edge, existingEdges: Edge[]): EdgeChange | null { + if (existingEdges.some((e) => e.id === edge.id)) { + return null; + } + return { + type: "add", + item: edge, + }; +} + +function Graph({ + subs, + onVariable, + onKeydown, +}: VariablesGraphProps): JSX.Element { + const instance = useReactFlow(); + const initialNodes: Node[] = []; + const initialEdges: Edge[] = []; + + const [direction, setDirection] = useState(DEFAULT_DIRECTION); + const [elements, setElements] = useState({ + nodes: initialNodes, + edges: initialEdges, + }); + + const refit = useCallback(() => { + window.requestAnimationFrame(() => { + instance.fitView(); + }); + }, [instance]); + + const onLayoutChange = useCallback( + (newDirection: GraphDirection) => { + setDirection(newDirection); + + setElements(({ nodes, edges }) => { + return computeLayoutedElements({ + direction: newDirection, + nodes, + edges, + }); + }); + refit(); + }, + [refit] + ); + + const onNodesChange = useCallback((changes: NodeChange[]) => { + setElements(({ nodes, edges }) => { + return { + nodes: applyNodeChanges(changes, nodes), + edges, + }; + }); + }, []); + + const onEdgesChange = useCallback((changes: EdgeChange[]) => { + setElements(({ nodes, edges }) => { + return { + nodes, + edges: applyEdgeChanges(changes, edges), + }; + }); + }, []); + + const addSubVariableLink = useCallback( + (fromN: Variable, subLinkN: Variable) => { + fromN = subs.get_root_key(fromN); + subLinkN = subs.get_root_key(subLinkN); + const from = fromN.toString(); + const to = subLinkN.toString(); + + setElements(({ nodes, edges }) => { + const optNewNode = addNodeChange( + newVariable(to, { + subs, + variable: subLinkN, + addSubVariableLink, + }), + nodes + ); + const newNodes = optNewNode + ? applyNodeChanges([optNewNode], nodes) + : nodes; + + const optNewEdge = addEdgeChange( + { id: `${from}->${to}`, source: from, target: to }, + edges + ); + const newEdges = optNewEdge + ? applyEdgeChanges([optNewEdge], edges) + : edges; + + return computeLayoutedElements({ + direction: "TB", + nodes: newNodes, + edges: newEdges, + }); + }); + + refit(); + }, + [subs, refit] + ); + + const addNode = useCallback( + (variableN: Variable) => { + variableN = subs.get_root_key(variableN); + const variable = variableN.toString(); + + setElements(({ nodes, edges }) => { + const optNewNode = addNodeChange( + newVariable(variable, { + subs, + variable: variableN, + addSubVariableLink, + }), + nodes + ); + const newNodes = optNewNode + ? applyNodeChanges([optNewNode], nodes) + : nodes; + + return computeLayoutedElements({ + direction: "TB", + nodes: newNodes, + edges, + }); + }); + + refit(); + }, + [subs, refit, addSubVariableLink] + ); + + onVariable(addNode); + onKeydown((key) => { + switch (key) { + case "c": { + onLayoutChange(direction); + } + } + }); + + return ( + onNodesChange(e)} + onEdgesChange={(e) => onEdgesChange(e)} + fitView + nodesDraggable + nodesConnectable={false} + nodeTypes={NODE_TYPES} + proOptions={{ + // https://reactflow.dev/docs/guides/remove-attribution/ + hideAttribution: true, + }} + > + + onLayoutChange(e)} + /> + + + + + ); +} + +interface DirectionPanelProps { + direction: GraphDirection; + onChange: (direction: GraphDirection) => void; +} + +function DirectionPanel({ + direction, + onChange, +}: DirectionPanelProps): JSX.Element { + const commonStyle = "rounded cursor-pointer text-2xl select-none"; + + const dirs: { dir: GraphDirection; text: string }[] = [ + { dir: "TB", text: "⬇️" }, + //{ dir: "LR", text: "➡️" }, + ]; + + return ( + <> + {dirs.map(({ dir, text }, i) => ( + { + onChange(dir); + }} + > + {text} + + ))} + + ); +} + +export default function VariablesGraph(props: VariablesGraphProps) { + return ( + + + + ); +} diff --git a/crates/compiler/checkmate/www/src/components/Ui.tsx b/crates/compiler/checkmate/www/src/components/Ui.tsx new file mode 100644 index 00000000000..8b288ec5b67 --- /dev/null +++ b/crates/compiler/checkmate/www/src/components/Ui.tsx @@ -0,0 +1,64 @@ +import React from "react"; +import { AllEvents, Variable } from "../schema"; +import { Engine } from "../engine/engine"; +import EventList from "./EventList"; +import VariablesGraph from "./Graph/VariablesGraph"; +import { TypedEmitter } from "tiny-typed-emitter"; +import { KeydownHandler, ToggleVariableHandler } from "./Events"; + +interface UiProps { + events: AllEvents; +} + +interface MessageEvents { + toggleVariable: ToggleVariableHandler; + keydown: KeydownHandler; +} + +export default function Ui({ events }: UiProps): JSX.Element { + const engine = new Engine(events); + + const ee = new TypedEmitter(); + const toggleVariableHandlers: ToggleVariableHandler[] = []; + const keydownHandlers: KeydownHandler[] = []; + ee.on("toggleVariable", (variable: Variable) => { + toggleVariableHandlers.forEach((handler) => handler(variable)); + }); + ee.on("keydown", (key: string) => { + keydownHandlers.forEach((handler) => handler(key)); + }); + + engine.stepTo(engine.lastEventIndex()); + const subs = engine.subs.snapshot(); + + return ( +
{ + ee.emit("keydown", e.key); + }} + > +
+ + ee.emit("toggleVariable", variable) + } + /> +
+
+ { + toggleVariableHandlers.push(handler); + }} + onKeydown={(handler) => { + keydownHandlers.push(handler); + }} + /> +
+
+ ); +} diff --git a/crates/compiler/checkmate/www/src/engine/engine.tsx b/crates/compiler/checkmate/www/src/engine/engine.tsx new file mode 100644 index 00000000000..d80e3d060ac --- /dev/null +++ b/crates/compiler/checkmate/www/src/engine/engine.tsx @@ -0,0 +1,146 @@ +import { Event, Variable } from "../schema"; +import { assertExhaustive } from "../utils/exhaustive"; +import { + ChangeEvent, + makeDeleteVariable, + makeRevertVariable, + RollbackChange, + Subs, +} from "./subs"; + +export type EventIndex = number & { __eventIndex: never }; + +function* flattenEvents(events: Event[]): Generator { + for (const event of events) { + yield event; + switch (event.type) { + case "Unification": { + yield* flattenEvents(event.subevents); + break; + } + case "VariableUnified": + case "VariableSetDescriptor": + break; + default: + assertExhaustive(event); + } + } +} + +function getFlatEvents(events: Event[]): { + flatEvents: Event[]; + map: Map; +} { + const map = new Map(); + const flatEvents = Array.from(flattenEvents(events)); + let i = 0; + for (const event of flatEvents) { + map.set(event, i as EventIndex); + i++; + } + return { flatEvents, map }; +} + +export class Engine { + #eventIndexMap: Map; + #events: Event[]; + #subs: Subs = Subs.new(); + #reverseEvents: Map = new Map(); + + #nextIndexForward: EventIndex = 0 as EventIndex; + + constructor(events: Event[]) { + const { flatEvents, map } = getFlatEvents(events); + this.#eventIndexMap = map; + this.#events = flatEvents; + } + + getEventIndex(event: Event): EventIndex { + const index = this.#eventIndexMap.get(event); + if (index === undefined) { + throw new Error("Event not found"); + } + return index; + } + + get step(): EventIndex { + return this.#nextIndexForward; + } + + stepTo(eventIndex: EventIndex): void { + while (this.#nextIndexForward <= eventIndex) { + this.stepForward(this.#nextIndexForward); + ++this.#nextIndexForward; + } + while (this.#nextIndexForward > eventIndex) { + --this.#nextIndexForward; + this.stepBackward(this.#nextIndexForward); + } + } + + get subs(): Readonly { + return this.#subs; + } + + lastEventIndex(): EventIndex { + return (this.#events.length - 1) as EventIndex; + } + + private stepForward(eventIndex: EventIndex): void { + const event = this.#events[eventIndex]; + if (!isApplicable(event)) { + return; + } + + if (!this.#reverseEvents.has(eventIndex)) { + const variable = applicableVariable(event); + const current = this.#subs.get(variable); + let revert: RollbackChange; + if (!current) { + revert = makeDeleteVariable({ variable }); + } else { + revert = makeRevertVariable({ variable, to: current }); + } + this.#reverseEvents.set(eventIndex, revert); + } + + this.#subs.apply(event); + } + + private stepBackward(eventIndex: EventIndex): void { + const event = this.#events[eventIndex]; + if (!isApplicable(event)) { + return; + } + + const revert = this.#reverseEvents.get(eventIndex); + if (!revert) { + throw new Error("No revert found"); + } + + this.#subs.apply(revert); + } +} + +function isApplicable(event: Event): event is ChangeEvent { + switch (event.type) { + case "VariableUnified": + case "VariableSetDescriptor": + return true; + case "Unification": + return false; + default: + assertExhaustive(event); + } +} + +function applicableVariable(event: ChangeEvent): Variable { + switch (event.type) { + case "VariableUnified": + return event.from; + case "VariableSetDescriptor": + return event.variable; + default: + assertExhaustive(event); + } +} diff --git a/crates/compiler/checkmate/www/src/engine/event_util.tsx b/crates/compiler/checkmate/www/src/engine/event_util.tsx new file mode 100644 index 00000000000..d04f78a9bc2 --- /dev/null +++ b/crates/compiler/checkmate/www/src/engine/event_util.tsx @@ -0,0 +1,19 @@ +import { Event } from "../schema"; + +export function lastSubEvent(event: Event): Event { + switch (event.type) { + case "Unification": { + const subevents = event.subevents; + if (subevents.length === 0) { + return event; + } + return lastSubEvent(event.subevents[event.subevents.length - 1]); + } + case "VariableUnified": { + return event; + } + case "VariableSetDescriptor": { + return event; + } + } +} diff --git a/crates/compiler/checkmate/www/src/engine/subs.tsx b/crates/compiler/checkmate/www/src/engine/subs.tsx new file mode 100644 index 00000000000..e1005e9e5cf --- /dev/null +++ b/crates/compiler/checkmate/www/src/engine/subs.tsx @@ -0,0 +1,176 @@ +import { Content, Rank, Variable, Event } from "../schema"; +import { assertExhaustive } from "../utils/exhaustive"; +import { Refine } from "../utils/refine"; + +export type TypeLink = { + type: "link"; + to: Variable; +}; + +function link({ to }: Omit): TypeLink { + return { type: "link", to }; +} + +export type TypeDescriptor = { + type: "descriptor"; + rank: Rank; + content: Content; +}; + +function descriptor({ + rank, + content, +}: Omit): TypeDescriptor { + return { type: "descriptor", rank, content }; +} + +export type VarType = TypeLink | TypeDescriptor; + +export type RevertVariableChange = { + type: "revertTo"; + variable: Variable; + to: VarType; +}; + +export type DeleteVariableChange = { + type: "delete"; + variable: Variable; +}; + +export type RollbackChange = RevertVariableChange | DeleteVariableChange; + +export function makeRevertVariable({ + variable, + to, +}: Omit): RevertVariableChange { + return { type: "revertTo", variable, to: { ...to } }; +} + +export function makeDeleteVariable({ + variable, +}: Omit): DeleteVariableChange { + return { type: "delete", variable }; +} + +export type ChangeEvent = + | Refine + | Refine; + +export type Change = ChangeEvent | RollbackChange; + +export class Subs implements QuerySubs { + #map: Map; + + private constructor(map: Map) { + this.#map = map; + } + + static new(): Subs { + return new Subs(new Map()); + } + + get(variable: Variable): VarType | undefined { + return this.#map.get(variable); + } + + get_root(variable: Variable): TypeDescriptor | undefined { + const type = this.get(variable); + if (type === undefined) { + return undefined; + } + switch (type.type) { + case "descriptor": + return type; + case "link": + return this.get_root(type.to); + default: + assertExhaustive(type); + } + } + + get_root_key(variable: Variable): Variable { + const type = this.get(variable); + if (type === undefined) { + return variable; + } + switch (type.type) { + case "descriptor": + return variable; + case "link": + return this.get_root_key(type.to); + default: + assertExhaustive(type); + } + } + + snapshot(): SubsSnapshot { + const snapshotMap = new Map(); + for (const [key, value] of this.#map) { + snapshotMap.set(key, { ...value }); + } + const snapshot = new Subs(snapshotMap); + return { + get(variable: Variable): VarType | undefined { + return snapshot.get(variable); + }, + get_root(variable: Variable): TypeDescriptor | undefined { + return snapshot.get_root(variable); + }, + get_root_key(variable: Variable): Variable { + return snapshot.get_root_key(variable); + }, + __snapshot__: SnapshotSymbol, + }; + } + + apply(change: Change): void { + switch (change.type) { + case "VariableUnified": { + const { from, to } = change; + if (from !== to) { + this.#map.set(from, link({ to })); + } + break; + } + case "VariableSetDescriptor": { + const { variable, rank, content } = change; + const existing = this.get_root(variable); + if (existing !== undefined) { + const nu = descriptor({ ...existing }); + if (rank) nu.rank = rank; + if (content) nu.content = content; + this.#map.set(variable, nu); + } else { + if (typeof rank !== "number") throw new Error("rank is required"); + if (!content) throw new Error("content is required"); + this.#map.set(variable, descriptor({ rank, content })); + } + break; + } + case "revertTo": { + const { variable, to } = change; + this.#map.set(variable, { ...to }); + break; + } + case "delete": { + const { variable } = change; + this.#map.delete(variable); + break; + } + default: + assertExhaustive(change); + } + } +} + +const SnapshotSymbol = Symbol("Snapshot"); + +export interface QuerySubs { + get(variable: Variable): VarType | undefined; + get_root(variable: Variable): TypeDescriptor | undefined; + get_root_key(variable: Variable): Variable; +} + +export interface SubsSnapshot extends QuerySubs { + __snapshot__: typeof SnapshotSymbol; +} diff --git a/crates/compiler/checkmate/www/src/schema.d.ts b/crates/compiler/checkmate/www/src/schema.d.ts index 405afe3b1d5..c956d6021d3 100644 --- a/crates/compiler/checkmate/www/src/schema.d.ts +++ b/crates/compiler/checkmate/www/src/schema.d.ts @@ -79,6 +79,10 @@ export type Content = unspecialized: UnspecializedClosureType[]; [k: string]: unknown; } + | { + type: "ErasedLambda"; + [k: string]: unknown; + } | { kind: AliasKind; name: Symbol; diff --git a/crates/compiler/checkmate/www/src/utils/exhaustive.ts b/crates/compiler/checkmate/www/src/utils/exhaustive.ts new file mode 100644 index 00000000000..476d9b09931 --- /dev/null +++ b/crates/compiler/checkmate/www/src/utils/exhaustive.ts @@ -0,0 +1,3 @@ +export function assertExhaustive(_: never): never { + throw new Error("Exhaustive switch"); +} diff --git a/crates/compiler/checkmate/www/src/utils/refine.ts b/crates/compiler/checkmate/www/src/utils/refine.ts new file mode 100644 index 00000000000..7be57f08568 --- /dev/null +++ b/crates/compiler/checkmate/www/src/utils/refine.ts @@ -0,0 +1,3 @@ +export type Refine = T & { + type: Type; +}; diff --git a/crates/compiler/checkmate/www/tailwind.config.js b/crates/compiler/checkmate/www/tailwind.config.js index fbc376404d6..c4e6ef71002 100644 --- a/crates/compiler/checkmate/www/tailwind.config.js +++ b/crates/compiler/checkmate/www/tailwind.config.js @@ -5,7 +5,12 @@ module.exports = { "./src/**/*.{js,jsx,ts,tsx}", ], theme: { - extend: {}, + extend: { + colors: { + 'roc-purple': '#7c38f5', + 'roc-purple-bg': '#ece2fd', + }, + }, }, plugins: [], } diff --git a/crates/compiler/checkmate/www/tsconfig.json b/crates/compiler/checkmate/www/tsconfig.json index 9d379a3c4af..2d519b5ad92 100644 --- a/crates/compiler/checkmate/www/tsconfig.json +++ b/crates/compiler/checkmate/www/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es5", + "target": "es2015", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true,