diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c9730dfa..f28c6c1e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -263,24 +263,6 @@ importers: '@algolia/client-search': specifier: ^4.17.0 version: 4.17.0 - '@codemirror/commands': - specifier: ^6.2.2 - version: 6.2.2 - '@codemirror/lang-javascript': - specifier: ^6.1.4 - version: 6.1.4 - '@codemirror/language': - specifier: ^6.6.0 - version: 6.6.0 - '@codemirror/lint': - specifier: ^6.2.0 - version: 6.2.0 - '@codemirror/state': - specifier: ^6.2.0 - version: 6.2.0 - '@codemirror/view': - specifier: ^6.9.3 - version: 6.9.3 '@docusaurus/core': specifier: 3.4.0 version: 3.4.0(@docusaurus/types@3.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(eslint@8.36.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4) @@ -317,24 +299,21 @@ importers: clsx: specifier: ^1.2.1 version: 1.2.1 - cm6-graphql: - specifier: ^0.0.3 - version: 0.0.3(@codemirror/language@6.6.0) glob: specifier: ^9.3.4 version: 9.3.4 graphql: specifier: ^16.6.0 version: 16.6.0 - graphql-language-service: - specifier: ^5.1.2 - version: 5.1.3(graphql@16.6.0) grats: specifier: workspace:* version: link:.. lz-string: specifier: ^1.5.0 version: 1.5.0 + monaco-editor: + specifier: ^0.51.0 + version: 0.51.0 netlify-cli: specifier: ^17.0.1 version: 17.0.1(@types/express@4.17.20)(@types/node@18.15.0)(picomatch@2.3.1) @@ -359,6 +338,9 @@ importers: react-dom: specifier: ^18.2.0 version: 18.2.0(react@18.2.0) + react-monaco-editor: + specifier: ^0.55.0 + version: 0.55.0(@types/react@18.0.33)(monaco-editor@0.51.0)(react@18.2.0) react-redux: specifier: ^8.0.5 version: 8.0.5(@types/react-dom@18.0.0)(@types/react@18.0.33)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(redux@4.2.1) @@ -378,15 +360,30 @@ importers: '@docusaurus/tsconfig': specifier: 3.4.0 version: 3.4.0 + css-loader: + specifier: ^7.1.2 + version: 7.1.2(webpack@5.78.0) + file-loader: + specifier: ^6.2.0 + version: 6.2.0(webpack@5.78.0) graphql-relay: specifier: ^0.10.0 version: 0.10.0(graphql@16.6.0) + monaco-editor-webpack-plugin: + specifier: ^7.1.0 + version: 7.1.0(monaco-editor@0.51.0)(webpack@5.78.0) + style-loader: + specifier: ^4.0.0 + version: 4.0.0(webpack@5.78.0) ts-node: specifier: ^10.9.2 version: 10.9.2(@types/node@18.15.0)(typescript@5.5.4) typescript: specifier: 5.5.4 version: 5.5.4 + worker-loader: + specifier: ^3.0.8 + version: 3.0.8(webpack@5.78.0) packages: @@ -1287,32 +1284,6 @@ packages: '@bugsnag/safe-json-stringify@6.0.0': resolution: {integrity: sha512-htzFO1Zc57S8kgdRK9mLcPVTW1BY2ijfH7Dk2CeZmspTWKdKqSo1iwmqrq2WtRjFlo8aRZYgLX0wFrDXF/9DLA==} - '@codemirror/autocomplete@6.4.2': - resolution: {integrity: sha512-8WE2xp+D0MpWEv5lZ6zPW1/tf4AGb358T5GWYiKEuCP8MvFfT3tH2mIF9Y2yr2e3KbHuSvsVhosiEyqCpiJhZQ==} - peerDependencies: - '@codemirror/language': ^6.0.0 - '@codemirror/state': ^6.0.0 - '@codemirror/view': ^6.0.0 - '@lezer/common': ^1.0.0 - - '@codemirror/commands@6.2.2': - resolution: {integrity: sha512-s9lPVW7TxXrI/7voZ+HmD/yiAlwAYn9PH5SUVSUhsxXHhv4yl5eZ3KLntSoTynfdgVYM0oIpccQEWRBQgmNZyw==} - - '@codemirror/lang-javascript@6.1.4': - resolution: {integrity: sha512-OxLf7OfOZBTMRMi6BO/F72MNGmgOd9B0vetOLvHsDACFXayBzW8fm8aWnDM0yuy68wTK03MBf4HbjSBNRG5q7A==} - - '@codemirror/language@6.6.0': - resolution: {integrity: sha512-cwUd6lzt3MfNYOobdjf14ZkLbJcnv4WtndYaoBkbor/vF+rCNguMPK0IRtvZJG4dsWiaWPcK8x1VijhvSxnstg==} - - '@codemirror/lint@6.2.0': - resolution: {integrity: sha512-KVCECmR2fFeYBr1ZXDVue7x3q5PMI0PzcIbA+zKufnkniMBo1325t0h1jM85AKp8l3tj67LRxVpZfgDxEXlQkg==} - - '@codemirror/state@6.2.0': - resolution: {integrity: sha512-69QXtcrsc3RYtOtd+GsvczJ319udtBf1PTrr2KbLWM/e2CXUPnh0Nz9AUo8WfhSQ7GeL8dPVNUmhQVgpmuaNGA==} - - '@codemirror/view@6.9.3': - resolution: {integrity: sha512-BJ5mvEIhFM+SrNwc5X8pLIvMM9ffjkviVbxpg84Xk2OE8ZyKaEbId8kX+nAYEEso7+qnbwsXe1bkAHsasebMow==} - '@colors/colors@1.5.0': resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} @@ -2000,18 +1971,6 @@ packages: '@leichtgewicht/ip-codec@2.0.4': resolution: {integrity: sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==} - '@lezer/common@1.0.2': - resolution: {integrity: sha512-SVgiGtMnMnW3ActR8SXgsDhw7a0w0ChHSYAyAUxxrOiJ1OqYWEKk/xJd84tTSPo1mo6DXLObAJALNnd0Hrv7Ng==} - - '@lezer/highlight@1.1.4': - resolution: {integrity: sha512-IECkFmw2l7sFcYXrV8iT9GeY4W0fU4CxX0WMwhmhMIVjoDdD1Hr6q3G2NqVtLg/yVe5n7i4menG3tJ2r4eCrPQ==} - - '@lezer/javascript@1.4.1': - resolution: {integrity: sha512-Hqx36DJeYhKtdpc7wBYPR0XF56ZzIp0IkMO/zNNj80xcaFOV4Oj/P7TQc/8k2TxNhzl7tV5tXS8ZOCPbT4L3nA==} - - '@lezer/lr@1.3.3': - resolution: {integrity: sha512-JPQe3mwJlzEVqy67iQiiGozhcngbO8QBgpqZM6oL1Wj/dXckrEexpBLeFkq0edtW5IqnPRFxA24BHJni8Js69w==} - '@lukeed/ms@2.0.1': resolution: {integrity: sha512-Xs/4RZltsAL7pkvaNStUQt7netTkyxrS0K+RILcVr3TRMS/ToOg4I6uNfhB9SlGsnWBym4U+EaXq0f0cEMNkHA==} engines: {node: '>=8'} @@ -3871,11 +3830,6 @@ packages: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} - cm6-graphql@0.0.3: - resolution: {integrity: sha512-ZN5xtt7Em3y3xZvACGVoFp5GN1Mvts1A9UH38ZHjal2ICJOx/o8ljMk1WlIz0ZGNV4rY7uUdVH7qxyIBTYU+RQ==} - peerDependencies: - '@codemirror/language': 6.0.0 - code-point-at@1.1.0: resolution: {integrity: sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==} engines: {node: '>=0.10.0'} @@ -4113,9 +4067,6 @@ packages: create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - crelt@1.0.5: - resolution: {integrity: sha512-+BO9wPPi+DWTDcNYhr/W90myha8ptzftZT+LwcmUbbok0rcP/fequmFYCw8NMoH7pkAZQzU78b3kYrlua5a9eA==} - cron-parser@4.8.1: resolution: {integrity: sha512-jbokKWGcyU4gl6jAfX97E1gDpY12DJ1cLJZmoDzaAln/shZ+S3KBFBuA2Q6WeUN4gJf/8klnV1EfvhA2lK5IRQ==} engines: {node: '>=12.0.0'} @@ -4150,6 +4101,18 @@ packages: webpack: optional: true + css-loader@7.1.2: + resolution: {integrity: sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==} + engines: {node: '>= 18.12.0'} + peerDependencies: + '@rspack/core': 0.x || 1.x + webpack: ^5.27.0 + peerDependenciesMeta: + '@rspack/core': + optional: true + webpack: + optional: true + css-minimizer-webpack-plugin@5.0.1: resolution: {integrity: sha512-3caImjKFQkS+ws1TGcFn0V1HyDJFq1Euy589JlD6/3rV2kj+w7r5G9WDMgSHvpvXHNZ2calVypZWuEDQd9wfLg==} engines: {node: '>= 14.15.0'} @@ -5367,12 +5330,6 @@ packages: peerDependencies: graphql: '>=0.11 <=16' - graphql-language-service@5.1.3: - resolution: {integrity: sha512-01KZLExoF53i8a2Jhgt+nVGsm9O2+jmDdwms4THSxCY+gU9FukF6X4pPLO2Gk7qZ6CVcInM8+IQx/ph4AOTOLA==} - hasBin: true - peerDependencies: - graphql: ^15.5.0 || ^16.0.0 - graphql-relay@0.10.0: resolution: {integrity: sha512-44yBuw2/DLNEiMypbNZBt1yMDbBmyVPVesPywnteGGALiBmdyy1JP8jSg8ClLePg8ZZxk0O4BLhd1a6U/1jDOQ==} engines: {node: ^12.20.0 || ^14.15.0 || >= 15.9.0} @@ -6758,6 +6715,15 @@ packages: moize@6.1.5: resolution: {integrity: sha512-Fu46qKV9F8DOi2vXimR3yRw/JAJfFRQEFZeclvOFnG92AEFERqwFtu4PIxETYFtCghHGlU1itKcvvNioKgWGIw==} + monaco-editor-webpack-plugin@7.1.0: + resolution: {integrity: sha512-ZjnGINHN963JQkFqjjcBtn1XBtUATDZBMgNQhDQwd78w2ukRhFXAPNgWuacaQiDZsUr4h1rWv5Mv6eriKuOSzA==} + peerDependencies: + monaco-editor: '>= 0.31.0' + webpack: ^4.5.0 || 5.x + + monaco-editor@0.51.0: + resolution: {integrity: sha512-xaGwVV1fq343cM7aOYB6lVE4Ugf0UyimdD/x5PWcWBMKENwectaEu77FAN7c5sFiyumqeJdX1RPTh1ocioyDjw==} + move-file@3.0.0: resolution: {integrity: sha512-v6u4XjX3MFW6Jo1V/YfbhC7eiGSgvYPJ/NM+aGtTtB9/Y6IYj7YViaHu6dkgDsZFB7MbnAoSI5+Z26XZXnP0vg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -6969,9 +6935,6 @@ packages: nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} - nullthrows@1.1.1: - resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==} - number-is-nan@1.0.1: resolution: {integrity: sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==} engines: {node: '>=0.10.0'} @@ -7515,10 +7478,6 @@ packages: peerDependencies: postcss: ^8.4.31 - postcss-selector-parser@6.0.11: - resolution: {integrity: sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==} - engines: {node: '>=4'} - postcss-selector-parser@6.1.1: resolution: {integrity: sha512-b4dlw/9V8A71rLIDsSwVmak9z2DuBUB7CA1/wSdelNEzqsjoSPeADTWNO09lpH49Diy3/JIZ2bSPB1dI3LJCHg==} engines: {node: '>=4'} @@ -7786,6 +7745,13 @@ packages: react-loadable: '*' webpack: '>=4.41.1 || 5.x' + react-monaco-editor@0.55.0: + resolution: {integrity: sha512-GdEP0Q3Rn1dczfKEEyY08Nes5plWwIYU4sWRBQO0+jsQWQsKMHKCC6+hPRwR7G/4aA3V/iU9jSmWPzVJYMVFSQ==} + peerDependencies: + '@types/react': '>=16 <= 18' + monaco-editor: ^0.44.0 + react: '>=16 <= 18' + react-redux@8.0.5: resolution: {integrity: sha512-Q2f6fCKxPFpkXt1qNRZdEDLlScsDWyrgSj0mliK59qU6W5gvBiKkdMEG2lJzhd1rCctf0hb6EtePPLZ2e0m1uw==} peerDependencies: @@ -8119,10 +8085,6 @@ packages: resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} engines: {node: '>= 10.13.0'} - schema-utils@4.0.0: - resolution: {integrity: sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==} - engines: {node: '>= 12.13.0'} - schema-utils@4.2.0: resolution: {integrity: sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==} engines: {node: '>= 12.13.0'} @@ -8495,8 +8457,11 @@ packages: resolution: {integrity: sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==} engines: {node: '>=14.16'} - style-mod@4.0.2: - resolution: {integrity: sha512-C4myMmRTO8iaC5Gg+N1ftK2WT4eXUTMAa+HEFPPrfVeO/NtqLTtAmV1HbqnuGtLwCek44Ra76fdGUkSqjiMPcQ==} + style-loader@4.0.0: + resolution: {integrity: sha512-1V4WqhhZZgjVAVJyt7TdDPZoPBPNHbekX4fWnCJL1yQukhCeZhJySUL+gL9y6sNdN95uEOS83Y55SqHcP7MzLA==} + engines: {node: '>= 18.12.0'} + peerDependencies: + webpack: ^5.27.0 style-to-object@0.4.4: resolution: {integrity: sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==} @@ -9056,12 +9021,6 @@ packages: vfile@6.0.2: resolution: {integrity: sha512-zND7NlS8rJYb/sPqkb13ZvbbUoExdbi4w3SfRrMq6R3FvnLQmmfpajJNITuuYm6AZ5uao9vy4BAos3EXBPf2rg==} - vscode-languageserver-types@3.17.3: - resolution: {integrity: sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA==} - - w3c-keyname@2.2.6: - resolution: {integrity: sha512-f+fciywl1SJEniZHD6H+kUO8gOnwIr7f4ijKA6+ZvJFjeGi1r4PDLl53Ayud9O/rk64RqgoQine0feoeOU0kXg==} - wait-port@1.0.4: resolution: {integrity: sha512-w8Ftna3h6XSFWWc2JC5gZEgp64nz8bnaTp5cvzbJSZ53j+omktWTDdwXxEF0jM8YveviLgFWvNGrSvRHnkyHyw==} engines: {node: '>=10'} @@ -9202,6 +9161,12 @@ packages: resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} engines: {node: '>=0.10.0'} + worker-loader@3.0.8: + resolution: {integrity: sha512-XQyQkIFeRVC7f7uRhFdNMe/iJOdO6zxAaR3EWbDp45v3mDhrTi+++oswKNxShUNjPC/1xUp5DB29YKLhFo129g==} + engines: {node: '>= 10.13.0'} + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + wrap-ansi@3.0.1: resolution: {integrity: sha512-iXR3tDXpbnTpzjKSylUJRkLuOrEC7hwEB221cgn6wtF8wpmz28puFXAEfPT5zrjM3wahygB//VuWEr1vTkDcNQ==} engines: {node: '>=4'} @@ -10470,53 +10435,6 @@ snapshots: '@bugsnag/safe-json-stringify@6.0.0': {} - '@codemirror/autocomplete@6.4.2(@codemirror/language@6.6.0)(@codemirror/state@6.2.0)(@codemirror/view@6.9.3)(@lezer/common@1.0.2)': - dependencies: - '@codemirror/language': 6.6.0 - '@codemirror/state': 6.2.0 - '@codemirror/view': 6.9.3 - '@lezer/common': 1.0.2 - - '@codemirror/commands@6.2.2': - dependencies: - '@codemirror/language': 6.6.0 - '@codemirror/state': 6.2.0 - '@codemirror/view': 6.9.3 - '@lezer/common': 1.0.2 - - '@codemirror/lang-javascript@6.1.4': - dependencies: - '@codemirror/autocomplete': 6.4.2(@codemirror/language@6.6.0)(@codemirror/state@6.2.0)(@codemirror/view@6.9.3)(@lezer/common@1.0.2) - '@codemirror/language': 6.6.0 - '@codemirror/lint': 6.2.0 - '@codemirror/state': 6.2.0 - '@codemirror/view': 6.9.3 - '@lezer/common': 1.0.2 - '@lezer/javascript': 1.4.1 - - '@codemirror/language@6.6.0': - dependencies: - '@codemirror/state': 6.2.0 - '@codemirror/view': 6.9.3 - '@lezer/common': 1.0.2 - '@lezer/highlight': 1.1.4 - '@lezer/lr': 1.3.3 - style-mod: 4.0.2 - - '@codemirror/lint@6.2.0': - dependencies: - '@codemirror/state': 6.2.0 - '@codemirror/view': 6.9.3 - crelt: 1.0.5 - - '@codemirror/state@6.2.0': {} - - '@codemirror/view@6.9.3': - dependencies: - '@codemirror/state': 6.2.0 - style-mod: 4.0.2 - w3c-keyname: 2.2.6 - '@colors/colors@1.5.0': {} '@colors/colors@1.6.0': {} @@ -10620,7 +10538,7 @@ snapshots: terser-webpack-plugin: 5.3.10(webpack@5.90.3) tslib: 2.6.2 update-notifier: 6.0.2 - url-loader: 4.1.1(file-loader@6.2.0(webpack@5.78.0))(webpack@5.90.3) + url-loader: 4.1.1(file-loader@6.2.0(webpack@5.90.3))(webpack@5.90.3) webpack: 5.90.3 webpack-bundle-analyzer: 4.10.2 webpack-dev-server: 4.15.2(webpack@5.90.3) @@ -10682,7 +10600,7 @@ snapshots: tslib: 2.6.2 unified: 11.0.5 unist-util-visit: 5.0.0 - url-loader: 4.1.1(file-loader@6.2.0(webpack@5.78.0))(webpack@5.90.3) + url-loader: 4.1.1(file-loader@6.2.0(webpack@5.90.3))(webpack@5.90.3) vfile: 6.0.2 webpack: 5.90.3 transitivePeerDependencies: @@ -11228,7 +11146,7 @@ snapshots: resolve-pathname: 3.0.0 shelljs: 0.8.5 tslib: 2.6.2 - url-loader: 4.1.1(file-loader@6.2.0(webpack@5.78.0))(webpack@5.90.3) + url-loader: 4.1.1(file-loader@6.2.0(webpack@5.90.3))(webpack@5.90.3) utility-types: 3.10.0 webpack: 5.90.3 optionalDependencies: @@ -11707,21 +11625,6 @@ snapshots: '@leichtgewicht/ip-codec@2.0.4': {} - '@lezer/common@1.0.2': {} - - '@lezer/highlight@1.1.4': - dependencies: - '@lezer/common': 1.0.2 - - '@lezer/javascript@1.4.1': - dependencies: - '@lezer/highlight': 1.1.4 - '@lezer/lr': 1.3.3 - - '@lezer/lr@1.3.3': - dependencies: - '@lezer/common': 1.0.2 - '@lukeed/ms@2.0.1': {} '@mapbox/node-pre-gyp@1.0.10(supports-color@9.3.1)': @@ -13658,7 +13561,7 @@ snapshots: dependencies: '@babel/core': 7.24.0 find-cache-dir: 4.0.0 - schema-utils: 4.0.0 + schema-utils: 4.2.0 webpack: 5.90.3 babel-plugin-dynamic-import-node@2.3.3: @@ -14103,10 +14006,6 @@ snapshots: clsx@2.1.1: {} - cm6-graphql@0.0.3(@codemirror/language@6.6.0): - dependencies: - '@codemirror/language': 6.6.0 - code-point-at@1.1.0: {} collapse-white-space@2.1.0: {} @@ -14290,7 +14189,7 @@ snapshots: glob-parent: 6.0.2 globby: 13.1.3 normalize-path: 3.0.0 - schema-utils: 4.0.0 + schema-utils: 4.2.0 serialize-javascript: 6.0.1 webpack: 5.90.3 @@ -14367,8 +14266,6 @@ snapshots: create-require@1.1.1: {} - crelt@1.0.5: {} - cron-parser@4.8.1: dependencies: luxon: 3.3.0 @@ -14408,6 +14305,19 @@ snapshots: optionalDependencies: webpack: 5.90.3 + css-loader@7.1.2(webpack@5.78.0): + dependencies: + icss-utils: 5.1.0(postcss@8.4.41) + postcss: 8.4.41 + postcss-modules-extract-imports: 3.1.0(postcss@8.4.41) + postcss-modules-local-by-default: 4.0.5(postcss@8.4.41) + postcss-modules-scope: 3.2.0(postcss@8.4.41) + postcss-modules-values: 4.0.0(postcss@8.4.41) + postcss-value-parser: 4.2.0 + semver: 7.5.4 + optionalDependencies: + webpack: 5.78.0 + css-minimizer-webpack-plugin@5.0.1(clean-css@5.3.2)(webpack@5.90.3): dependencies: '@jridgewell/trace-mapping': 0.3.25 @@ -14870,8 +14780,8 @@ snapshots: detective-postcss@6.1.3: dependencies: is-url: 1.2.4 - postcss: 8.4.35 - postcss-values-parser: 6.0.2(postcss@8.4.35) + postcss: 8.4.41 + postcss-values-parser: 6.0.2(postcss@8.4.41) detective-sass@5.0.3: dependencies: @@ -15525,6 +15435,12 @@ snapshots: dependencies: flat-cache: 3.0.4 + file-loader@6.2.0(webpack@5.78.0): + dependencies: + loader-utils: 2.0.4 + schema-utils: 3.3.0 + webpack: 5.78.0 + file-loader@6.2.0(webpack@5.90.3): dependencies: loader-utils: 2.0.4 @@ -15871,12 +15787,6 @@ snapshots: dependencies: graphql: 16.8.1 - graphql-language-service@5.1.3(graphql@16.6.0): - dependencies: - graphql: 16.6.0 - nullthrows: 1.1.1 - vscode-languageserver-types: 3.17.3 - graphql-relay@0.10.0(graphql@16.6.0): dependencies: graphql: 16.6.0 @@ -16260,6 +16170,10 @@ snapshots: dependencies: postcss: 8.4.35 + icss-utils@5.1.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + ieee754@1.2.1: {} ignore@5.2.4: {} @@ -17649,7 +17563,7 @@ snapshots: mini-css-extract-plugin@2.9.0(webpack@5.90.3): dependencies: - schema-utils: 4.0.0 + schema-utils: 4.2.0 tapable: 2.2.1 webpack: 5.90.3 @@ -17707,6 +17621,14 @@ snapshots: fast-equals: 3.0.3 micro-memoize: 4.0.14 + monaco-editor-webpack-plugin@7.1.0(monaco-editor@0.51.0)(webpack@5.78.0): + dependencies: + loader-utils: 2.0.4 + monaco-editor: 0.51.0 + webpack: 5.78.0 + + monaco-editor@0.51.0: {} + move-file@3.0.0: dependencies: path-exists: 5.0.0 @@ -18048,8 +17970,6 @@ snapshots: dependencies: boolbase: 1.0.0 - nullthrows@1.1.1: {} - number-is-nan@1.0.1: {} object-assign@4.1.1: {} @@ -18393,13 +18313,13 @@ snapshots: postcss-calc@9.0.1(postcss@8.4.35): dependencies: postcss: 8.4.35 - postcss-selector-parser: 6.0.11 + postcss-selector-parser: 6.1.1 postcss-value-parser: 4.2.0 postcss-calc@9.0.1(postcss@8.4.41): dependencies: postcss: 8.4.41 - postcss-selector-parser: 6.0.11 + postcss-selector-parser: 6.1.1 postcss-value-parser: 4.2.0 postcss-colormin@6.1.0(postcss@8.4.35): @@ -18563,23 +18483,44 @@ snapshots: dependencies: postcss: 8.4.35 + postcss-modules-extract-imports@3.1.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-modules-local-by-default@4.0.5(postcss@8.4.35): dependencies: icss-utils: 5.1.0(postcss@8.4.35) postcss: 8.4.35 - postcss-selector-parser: 6.0.11 + postcss-selector-parser: 6.1.1 + postcss-value-parser: 4.2.0 + + postcss-modules-local-by-default@4.0.5(postcss@8.4.41): + dependencies: + icss-utils: 5.1.0(postcss@8.4.41) + postcss: 8.4.41 + postcss-selector-parser: 6.1.1 postcss-value-parser: 4.2.0 postcss-modules-scope@3.2.0(postcss@8.4.35): dependencies: postcss: 8.4.35 - postcss-selector-parser: 6.0.11 + postcss-selector-parser: 6.1.1 + + postcss-modules-scope@3.2.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-selector-parser: 6.1.1 postcss-modules-values@4.0.0(postcss@8.4.35): dependencies: icss-utils: 5.1.0(postcss@8.4.35) postcss: 8.4.35 + postcss-modules-values@4.0.0(postcss@8.4.41): + dependencies: + icss-utils: 5.1.0(postcss@8.4.41) + postcss: 8.4.41 + postcss-normalize-charset@6.0.2(postcss@8.4.35): dependencies: postcss: 8.4.35 @@ -18709,11 +18650,6 @@ snapshots: postcss: 8.4.41 postcss-value-parser: 4.2.0 - postcss-selector-parser@6.0.11: - dependencies: - cssesc: 3.0.0 - util-deprecate: 1.0.2 - postcss-selector-parser@6.1.1: dependencies: cssesc: 3.0.0 @@ -18748,11 +18684,11 @@ snapshots: postcss-value-parser@4.2.0: {} - postcss-values-parser@6.0.2(postcss@8.4.35): + postcss-values-parser@6.0.2(postcss@8.4.41): dependencies: color-name: 1.1.4 is-url-superb: 4.0.0 - postcss: 8.4.35 + postcss: 8.4.41 quote-unquote: 1.0.0 postcss-zindex@6.0.2(postcss@8.4.41): @@ -19028,6 +18964,13 @@ snapshots: react-loadable: '@docusaurus/react-loadable@6.0.0(react@18.2.0)' webpack: 5.90.3 + react-monaco-editor@0.55.0(@types/react@18.0.33)(monaco-editor@0.51.0)(react@18.2.0): + dependencies: + '@types/react': 18.0.33 + monaco-editor: 0.51.0 + prop-types: 15.8.1 + react: 18.2.0 + react-redux@8.0.5(@types/react-dom@18.0.0)(@types/react@18.0.33)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(redux@4.2.1): dependencies: '@babel/runtime': 7.21.0 @@ -19419,13 +19362,6 @@ snapshots: ajv: 6.12.6 ajv-keywords: 3.5.2(ajv@6.12.6) - schema-utils@4.0.0: - dependencies: - '@types/json-schema': 7.0.11 - ajv: 8.12.0 - ajv-formats: 2.1.1(ajv@8.12.0) - ajv-keywords: 5.1.0(ajv@8.12.0) - schema-utils@4.2.0: dependencies: '@types/json-schema': 7.0.11 @@ -19844,7 +19780,9 @@ snapshots: '@tokenizer/token': 0.3.0 peek-readable: 5.0.0 - style-mod@4.0.2: {} + style-loader@4.0.0(webpack@5.78.0): + dependencies: + webpack: 5.78.0 style-to-object@0.4.4: dependencies: @@ -19967,7 +19905,7 @@ snapshots: dependencies: '@jridgewell/trace-mapping': 0.3.25 jest-worker: 27.5.1 - schema-utils: 3.1.1 + schema-utils: 3.3.0 serialize-javascript: 6.0.1 terser: 5.16.8 webpack: 5.78.0 @@ -20317,7 +20255,7 @@ snapshots: urix@0.1.0: {} - url-loader@4.1.1(file-loader@6.2.0(webpack@5.78.0))(webpack@5.90.3): + url-loader@4.1.1(file-loader@6.2.0(webpack@5.90.3))(webpack@5.90.3): dependencies: loader-utils: 2.0.4 mime-types: 2.1.35 @@ -20390,10 +20328,6 @@ snapshots: unist-util-stringify-position: 4.0.0 vfile-message: 4.0.2 - vscode-languageserver-types@3.17.3: {} - - w3c-keyname@2.2.6: {} - wait-port@1.0.4: dependencies: chalk: 4.1.2 @@ -20447,7 +20381,7 @@ snapshots: memfs: 3.4.13 mime-types: 2.1.35 range-parser: 1.2.1 - schema-utils: 4.0.0 + schema-utils: 4.2.0 webpack: 5.90.3 webpack-dev-server@4.15.2(webpack@5.90.3): @@ -20475,7 +20409,7 @@ snapshots: open: 8.4.2 p-retry: 4.6.2 rimraf: 3.0.2 - schema-utils: 4.0.0 + schema-utils: 4.2.0 selfsigned: 2.1.1 serve-index: 1.9.1 sockjs: 0.3.24 @@ -20629,6 +20563,12 @@ snapshots: word-wrap@1.2.3: {} + worker-loader@3.0.8(webpack@5.78.0): + dependencies: + loader-utils: 2.0.4 + schema-utils: 3.3.0 + webpack: 5.78.0 + wrap-ansi@3.0.1: dependencies: string-width: 2.1.1 diff --git a/src/utils/DiagnosticError.ts b/src/utils/DiagnosticError.ts index 833f88eb..8383eb90 100644 --- a/src/utils/DiagnosticError.ts +++ b/src/utils/DiagnosticError.ts @@ -2,7 +2,7 @@ import { GraphQLError, Location, Source } from "graphql"; import * as ts from "typescript"; import { Result } from "./Result"; -type FixableDiagnostic = ts.Diagnostic & { +export type FixableDiagnostic = ts.Diagnostic & { fix?: ts.CodeFixAction; }; export type FixableDiagnosticWithLocation = ts.DiagnosticWithLocation & { diff --git a/website/package.json b/website/package.json index 2f72dde0..631bf13d 100644 --- a/website/package.json +++ b/website/package.json @@ -18,12 +18,6 @@ }, "dependencies": { "@algolia/client-search": "^4.17.0", - "@codemirror/commands": "^6.2.2", - "@codemirror/lang-javascript": "^6.1.4", - "@codemirror/language": "^6.6.0", - "@codemirror/lint": "^6.2.0", - "@codemirror/state": "^6.2.0", - "@codemirror/view": "^6.9.3", "@docusaurus/core": "3.4.0", "@docusaurus/preset-classic": "3.4.0", "@docusaurus/theme-classic": "3.4.0", @@ -36,12 +30,11 @@ "@types/react": "^18", "@typescript/vfs": "1.6.0", "clsx": "^1.2.1", - "cm6-graphql": "^0.0.3", "glob": "^9.3.4", "graphql": "^16.6.0", - "graphql-language-service": "^5.1.2", "grats": "workspace:*", "lz-string": "^1.5.0", + "monaco-editor": "^0.51.0", "netlify-cli": "^17.0.1", "path-browserify": "^1.0.1", "prettier": "^2.8.7", @@ -50,6 +43,7 @@ "raw-loader": "^4.0.2", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-monaco-editor": "^0.55.0", "react-redux": "^8.0.5", "redux": "^4.2.1", "reselect": "^4.1.7", @@ -59,9 +53,14 @@ "@docusaurus/module-type-aliases": "3.4.0", "@docusaurus/tsconfig": "3.4.0", "@types/react": "^18.2.29", + "css-loader": "^7.1.2", + "file-loader": "^6.2.0", "graphql-relay": "^0.10.0", + "monaco-editor-webpack-plugin": "^7.1.0", + "style-loader": "^4.0.0", "ts-node": "^10.9.2", - "typescript": "5.5.4" + "typescript": "5.5.4", + "worker-loader": "^3.0.8" }, "browserslist": { "production": [ diff --git a/website/plugins/monaco-editor.js b/website/plugins/monaco-editor.js new file mode 100644 index 00000000..ed567b9f --- /dev/null +++ b/website/plugins/monaco-editor.js @@ -0,0 +1,30 @@ +const MonacoWebpackPlugin = require("monaco-editor-webpack-plugin"); + +module.exports = function (_context, _options) { + return { + name: "monaco-editor", + configureWebpack(_config, _isServer) { + return { + module: { + rules: [ + /* + { + test: /\.ttf$/, + use: ["file-loader"], + }, + */ + { + test: /\.ttf$/, + type: "asset/resource", + }, + ], + }, + plugins: [ + new MonacoWebpackPlugin({ + languages: ["typescript", "graphql"], + }), + ], + }; + }, + }; +}; diff --git a/website/plugins/webpack.js b/website/plugins/webpack.js index f134ba37..e27aed34 100644 --- a/website/plugins/webpack.js +++ b/website/plugins/webpack.js @@ -1,3 +1,5 @@ +const MonacoWebpackPlugin = require("monaco-editor-webpack-plugin"); + module.exports = function (_context, _options) { return { name: "custom-docusaurus-plugin", @@ -11,6 +13,25 @@ module.exports = function (_context, _options) { node: { __dirname: "mock", }, + module: { + rules: [ + /* + { + test: /monaco.*\.css$/, + use: ["style-loader", "css-loader"], + }, + { + test: /\.ttf$/, + type: "asset/resource", + }, + */ + ], + }, + plugins: [ + new MonacoWebpackPlugin({ + languages: ["typescript", "graphql"], + }), + ], }; }, }; diff --git a/website/src/components/MonacoPlayground/BothEditors.tsx b/website/src/components/MonacoPlayground/BothEditors.tsx new file mode 100644 index 00000000..daa072a0 --- /dev/null +++ b/website/src/components/MonacoPlayground/BothEditors.tsx @@ -0,0 +1,11 @@ +import InputEditor from "./InputEditor"; +import OutputEditor from "./OutputEditor"; + +export default function BothEditors() { + return ( + <> + + + + ); +} diff --git a/website/src/components/MonacoPlayground/InputEditor.tsx b/website/src/components/MonacoPlayground/InputEditor.tsx new file mode 100644 index 00000000..758518ce --- /dev/null +++ b/website/src/components/MonacoPlayground/InputEditor.tsx @@ -0,0 +1,92 @@ +import React, { useEffect, useState } from "react"; +import store, { getDoc, useAppSelector } from "../PlaygroundFeatures/store"; +import { monaco as Monaco } from "react-monaco-editor"; +import { getDiagnostics } from "../PlaygroundFeatures/gratsStoreBindings"; +import { + codeActionsForDiagnostics, + resolveDiagnosticLocation, +} from "./diagnostics"; +import MonacoEditor from "./MonacoEditor"; + +export default function InputEditor() { + const diagnostics = useAppSelector(getDiagnostics); + const [editor, setEditor] = + useState(null); + const [monaco, setMonaco] = useState(null); + const doc = useAppSelector(getDoc); + + useEffect(() => { + if (editor == null || monaco == null) { + return; + } + if (diagnostics == null) { + return; + } + const model = editor.getModel(); + if (model == null) { + return; + } + const text = editor.getValue(); + const markers = diagnostics.map((diagnostic) => { + const { startLineNumber, startColumn, endLineNumber, endColumn } = + resolveDiagnosticLocation(text, diagnostic.start, diagnostic.length); + + return { + severity: monaco.MarkerSeverity.Error, + message: diagnostic.messageText, + startLineNumber, + startColumn, + endLineNumber, + endColumn, + }; + }); + monaco.editor.setModelMarkers(model, "owner", markers); + const disposable = monaco.languages.registerCodeActionProvider( + "typescript", + { + provideCodeActions: (model, _range, _context, _token) => { + const actions = codeActionsForDiagnostics(text, model, diagnostics); + return { + actions: actions, + dispose: () => { + // noop + }, + }; + }, + }, + ); + + return () => { + disposable.dispose(); + }; + }, [editor, monaco, diagnostics]); + + const newDoc = debounce((value) => { + store.dispatch({ type: "NEW_DOCUMENT_TEXT", value }); + }); + + return ( + { + newDoc(value); + }} + editorDidMount={(editor, monaco) => { + setEditor(editor); + setMonaco(monaco); + editor.focus(); + }} + /> + ); +} + +function debounce(func, wait = 500) { + let timeout; + return function (...args) { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const context = this; + clearTimeout(timeout); + timeout = setTimeout(() => func.apply(context, args), wait); + }; +} diff --git a/website/src/components/MonacoPlayground/MonacoEditor.tsx b/website/src/components/MonacoPlayground/MonacoEditor.tsx new file mode 100644 index 00000000..7b19c269 --- /dev/null +++ b/website/src/components/MonacoPlayground/MonacoEditor.tsx @@ -0,0 +1,81 @@ +import React, { useRef, useState, useEffect } from "react"; +import MonacoEditorImpl, { MonacoEditorProps } from "react-monaco-editor"; +import { useColorMode } from "@docusaurus/theme-common"; + +export default function MonacoEditor(props: MonacoEditorProps) { + const { colorMode } = useColorMode(); + + function onEditorWillMount(monaco) { + const vsDarkTheme = { + base: "vs-dark", + inherit: true, + rules: [{ background: "121212" }], + colors: { + "editor.background": "#121212", + }, + }; + + monaco.editor.defineTheme("vs-dark", vsDarkTheme); + + if (props.editorWillMount) { + props.editorWillMount(monaco); + } + } + + return ( +
+ + {({ width, height }) => ( + { + if (props.editorDidMount) { + props.editorDidMount(editor, monaco); + } + }} + theme={colorMode === "dark" ? "vs-dark" : "vs-light"} + /> + )} + +
+ ); +} + +const ResizableComponent = ({ children }) => { + const ref = useRef(null); + const [dimensions, setDimensions] = useState({ width: 0, height: 0 }); + + useEffect(() => { + const updateDimensions = () => { + if (ref.current) { + setDimensions({ + // @ts-ignore + width: ref.current.offsetWidth, + // @ts-ignore + height: ref.current.offsetHeight, + }); + } + }; + + updateDimensions(); // Initial measurement + window.addEventListener("resize", updateDimensions); // Update on window resize + + return () => { + window.removeEventListener("resize", updateDimensions); // Cleanup + }; + }, []); + + return ( +
+ {children({ ...dimensions })} +
+ ); +}; diff --git a/website/src/components/MonacoPlayground/OutputEditor.tsx b/website/src/components/MonacoPlayground/OutputEditor.tsx new file mode 100644 index 00000000..c9a12bf7 --- /dev/null +++ b/website/src/components/MonacoPlayground/OutputEditor.tsx @@ -0,0 +1,62 @@ +import React from "react"; +import { getOutputOption, useAppSelector } from "../PlaygroundFeatures/store"; +import { + getErrorText, + getSchemaText, + getTsSchema, +} from "../PlaygroundFeatures/gratsStoreBindings"; +import MonacoEditor from "../MonacoPlayground/MonacoEditor"; + +/** + * If there are errors, show the error message as plain text. + * + * If the output option is "sdl", show the SDL. + * If the output option is "typescript", show the generated graphql-js implementation. + */ +export default function OutputEditor() { + const errorText = useAppSelector(getErrorText); + const outputOptions = useAppSelector(getOutputOption); + if (errorText) { + return ; + } + switch (outputOptions) { + case "sdl": + return ; + case "typescript": + return ; + default: + return null; + } +} + +function ErrorsEditor({ errorText }) { + return ( + + ); +} + +function TsSchemaEditor() { + const tsSchema = useAppSelector(getTsSchema); + return ( + + ); +} + +function SchemaEditor() { + const schemaText = useAppSelector(getSchemaText); + return ( + + ); +} diff --git a/website/src/components/MonacoPlayground/diagnostics.ts b/website/src/components/MonacoPlayground/diagnostics.ts new file mode 100644 index 00000000..47d1d7bb --- /dev/null +++ b/website/src/components/MonacoPlayground/diagnostics.ts @@ -0,0 +1,82 @@ +import { languages } from "monaco-editor"; +import { monaco as Monaco } from "react-monaco-editor"; +import { FixableDiagnostic } from "../../../../src/utils/DiagnosticError"; + +export function codeActionsForDiagnostics( + text: string, + model: Monaco.editor.ITextModel, + diagnostics: FixableDiagnostic[], +): languages.CodeAction[] { + return diagnostics + .filter((d) => d.fix != null) + .flatMap((diagnostic) => { + if (diagnostic.fix == null) { + return []; + } + const fixName = diagnostic.fix.fixName; + return diagnostic.fix.changes.flatMap((change) => { + return change.textChanges.map((textChange) => { + const range = resolveDiagnosticLocation( + text, + textChange.span.start, + textChange.span.length, + ); + const textEdit: languages.TextEdit = { + range, + text: textChange.newText, + }; + + const edit: languages.IWorkspaceTextEdit = { + resource: model.uri, + textEdit, + versionId: model.getAlternativeVersionId(), + }; + return { + title: fixName, + kind: "quickfix", + edit: { edits: [edit] }, + isPreferred: true, + }; + }); + }); + }); +} + +export function resolveDiagnosticLocation( + text: string, + start: number, + length: number, +): Monaco.Range { + const lines = text.split("\n"); + let startLine = 0; + let startColumn = 0; + let endLine = 0; + let endColumn = 0; + let charIndex = 0; + let lineIndex = 0; + + for (const line of lines) { + if (charIndex + line.length >= start) { + startLine = lineIndex + 1; + startColumn = start - charIndex + 1; + break; + } + charIndex += line.length + 1; // +1 to account for the newline character + lineIndex += 1; + } + + lineIndex = 0; // Reset lineIndex but not charIndex + charIndex = 0; + + for (const line of lines) { + if (charIndex + line.length >= start + length) { + endLine = lineIndex + 1; + endColumn = start + length - charIndex + 1; + break; + } + charIndex += line.length + 1; // +1 to account for the newline character + lineIndex += 1; + } + + return new Monaco.Range(startLine, startColumn, endLine, endColumn); +} diff --git a/website/src/components/PlaygroundFeatures/ConfigBar.tsx b/website/src/components/PlaygroundFeatures/ConfigBar.tsx index 3400be10..8b5e4357 100644 --- a/website/src/components/PlaygroundFeatures/ConfigBar.tsx +++ b/website/src/components/PlaygroundFeatures/ConfigBar.tsx @@ -2,6 +2,7 @@ import React from "react"; import store, { getNullableByDefault, getOutputOption, + getSemanticNullability, getShowGratsDirectives, useAppSelector, } from "./store"; @@ -11,6 +12,7 @@ import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; export default function ConfigBar(): JSX.Element { const nullableByDefault = useAppSelector(getNullableByDefault); + const semanticNullability = useAppSelector(getSemanticNullability); const showGratsDirectives = useAppSelector(getShowGratsDirectives); const outputOption = useAppSelector(getOutputOption); const { gitHash } = useDocusaurusContext().siteConfig.customFields as { @@ -44,7 +46,20 @@ export default function ConfigBar(): JSX.Element { }); }} /> - Make fields nullable by default + Nullable by default + +
diff --git a/website/src/components/PlaygroundFeatures/FillRemainingHeight.tsx b/website/src/components/PlaygroundFeatures/FillRemainingHeight.tsx new file mode 100644 index 00000000..3f70ee12 --- /dev/null +++ b/website/src/components/PlaygroundFeatures/FillRemainingHeight.tsx @@ -0,0 +1,32 @@ +import { useLayoutEffect, useState } from "react"; +// On mount, measures the window height and current vertical offset, and +// renders children into a div that stretches to the bottom of the viewport. +export default function FillRemainingHeight({ children, minHeight }) { + const [containerRef, setContainerRef] = useState(null); + const [height, setHeight] = useState(null); + useLayoutEffect(() => { + if (containerRef == null) { + return; + } + + function updateSize() { + const verticalOffset = containerRef.getBoundingClientRect().y; + const available = Math.max( + window.innerHeight - verticalOffset, + minHeight, + ); + setHeight(available); + } + + updateSize(); + + window.addEventListener("resize", updateSize); + return () => window.removeEventListener("resize", updateSize); + }, [containerRef, minHeight]); + + return ( +
+ {height != null && children} +
+ ); +} diff --git a/website/src/components/PlaygroundFeatures/defaultState.ts b/website/src/components/PlaygroundFeatures/defaultState.ts index cbe4894c..767de741 100644 --- a/website/src/components/PlaygroundFeatures/defaultState.ts +++ b/website/src/components/PlaygroundFeatures/defaultState.ts @@ -42,6 +42,7 @@ export const DEFAULT_STATE: State = { outputOption: "sdl", showGratsDirectives: false, }, + ts: null, gratsResult: null, VERSION: URL_VERSION, }; diff --git a/website/src/components/PlaygroundFeatures/editors/CodegenOutputView.tsx b/website/src/components/PlaygroundFeatures/editors/CodegenOutputView.tsx deleted file mode 100644 index 2b5ca922..00000000 --- a/website/src/components/PlaygroundFeatures/editors/CodegenOutputView.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import React, { useState, useEffect } from "react"; -import { EditorState } from "@codemirror/state"; -import { EditorView, keymap, lineNumbers } from "@codemirror/view"; -import { defaultKeymap } from "@codemirror/commands"; -import { typescriptLanguage } from "@codemirror/lang-javascript"; -import { - defaultHighlightStyle, - syntaxHighlighting, -} from "@codemirror/language"; -import { getTypeScriptOutputString, onSelectorChange } from "../store"; -import { Theme } from "./theme"; -import store from "../store"; - -export default function OutputView() { - const [ref, setRef] = useState(null); - useEffect(() => { - if (ref != null) { - createOutputView(store, ref); - } - }, [ref]); - return ( -
- ); -} - -export function createOutputView(store, right: HTMLDivElement) { - const outputState = EditorState.create({ - doc: getTypeScriptOutputString(store.getState()), - extensions: [ - Theme, - keymap.of(defaultKeymap), - lineNumbers(), - typescriptLanguage, - EditorState.readOnly.of(true), - EditorView.lineWrapping, - syntaxHighlighting(defaultHighlightStyle, { fallback: true }), - ], - }); - - const rightView = new EditorView({ - state: outputState, - parent: right, - }); - - // When the output changes, update the output view - onSelectorChange(store, getTypeScriptOutputString, (output) => { - rightView.dispatch({ - changes: { - from: 0, - to: rightView.state.doc.length, - insert: output, - }, - }); - }); -} diff --git a/website/src/components/PlaygroundFeatures/editors/GraphQLOutputView.tsx b/website/src/components/PlaygroundFeatures/editors/GraphQLOutputView.tsx deleted file mode 100644 index 20079b3b..00000000 --- a/website/src/components/PlaygroundFeatures/editors/GraphQLOutputView.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import React, { useState, useEffect } from "react"; -import { EditorState } from "@codemirror/state"; -import { EditorView, keymap, lineNumbers } from "@codemirror/view"; -import { defaultKeymap } from "@codemirror/commands"; -import { graphqlLanguage } from "cm6-graphql"; -import { - defaultHighlightStyle, - syntaxHighlighting, -} from "@codemirror/language"; -import { getGraphQLOutputString, onSelectorChange } from "../store"; -import { Theme } from "./theme"; -import store from "../store"; - -export default function OutputView() { - const [ref, setRef] = useState(null); - useEffect(() => { - if (ref != null) { - createOutputView(store, ref); - } - }, [ref]); - return ( -
- ); -} - -export function createOutputView(store, right: HTMLDivElement) { - const outputState = EditorState.create({ - doc: getGraphQLOutputString(store.getState()), - extensions: [ - Theme, - keymap.of(defaultKeymap), - lineNumbers(), - graphqlLanguage, - EditorState.readOnly.of(true), - EditorView.lineWrapping, - syntaxHighlighting(defaultHighlightStyle, { fallback: true }), - ], - }); - - const rightView = new EditorView({ - state: outputState, - parent: right, - }); - - // When the output changes, update the output view - onSelectorChange(store, getGraphQLOutputString, (output) => { - rightView.dispatch({ - changes: { - from: 0, - to: rightView.state.doc.length, - insert: output, - }, - }); - }); -} diff --git a/website/src/components/PlaygroundFeatures/editors/InputView.tsx b/website/src/components/PlaygroundFeatures/editors/InputView.tsx deleted file mode 100644 index 0350addc..00000000 --- a/website/src/components/PlaygroundFeatures/editors/InputView.tsx +++ /dev/null @@ -1,132 +0,0 @@ -import React, { useState, useEffect } from "react"; -import { EditorState, Compartment } from "@codemirror/state"; -import { EditorView, keymap, lineNumbers } from "@codemirror/view"; -import { defaultKeymap, history, historyKeymap } from "@codemirror/commands"; -import { typescriptLanguage } from "@codemirror/lang-javascript"; -import * as ts from "typescript"; -import { - defaultHighlightStyle, - syntaxHighlighting, -} from "@codemirror/language"; -import { createLinter } from "../linter"; -import lzstring from "lz-string"; -import { createDefaultMapFromCDN } from "@typescript/vfs"; -import { getConfig, getDoc, getView, onSelectorChange } from "../store"; -import { createSelector } from "reselect"; -import { Theme } from "./theme"; -import store, { useUrlState } from "../store"; -import { GratsConfig } from "grats"; - -export default function InputView() { - useEffect(() => { - store.dispatch({ type: "SET_STATE_FROM_URL" }); - }, []); - useUrlState(store); - const [ref, setRef] = useState(null); - const fsMap = useFsMap(); - useEffect(() => { - if (ref != null && fsMap != null) { - createInputView(store, fsMap, ref); - } - }, [ref, fsMap]); - return ( -
- ); -} - -function useFsMap() { - const [fsMap, setFsMap] = useState | null>(null); - - useEffect(() => { - let unmounted = false; - const shouldCache = false; - createDefaultMapFromCDN( - { target: ts.ScriptTarget.ES2021, lib: ["es2021"] }, - ts.version, - shouldCache, - ts, - lzstring, - ).then((fsMap) => { - if (!unmounted) { - // There's some bug/mismatch where the files that are expected by our version of - // TypeScript don't exactly match those included in/accessible by the - // `@typescript/vfs` package. If it's missing a file it crashes, so we'll - // include them as empty for now. Their actual contents are just a few very - // obscure APIs, so it's probably fine to just leave them for now. - for (const fileName of [ - "/lib.es2016.intl.d.ts", - "/lib.dom.asynciterable.d.ts", - ]) { - fsMap.set(fileName, ""); - } - setFsMap(fsMap); - } - }); - - return () => { - unmounted = true; - }; - }, []); - - return fsMap; -} - -// TODO: Make this into hooks -async function createInputView(store, fsMap, left) { - const state = store.getState(); - - // Create a selector that memoizes the linter and closes over the fsMap - const getLinter = createSelector( - getView, - getConfig, - (view, config: GratsConfig) => { - return createLinter(fsMap, view, config); - }, - ); - - const linter = getLinter(state); - - const linterCompartment = new Compartment(); - const inputState = EditorState.create({ - doc: state.doc, - extensions: [ - Theme, - keymap.of(...defaultKeymap, ...historyKeymap), - history(), - linterCompartment.of(linter), - typescriptLanguage, - EditorView.lineWrapping, - lineNumbers(), - syntaxHighlighting(defaultHighlightStyle, { fallback: true }), - ], - }); - - const leftView = new EditorView({ state: inputState, parent: left }); - - // When the linter changes, update the linter - onSelectorChange(store, getDoc, (doc) => { - const state = leftView.state; - if (state.doc.toString() === doc) { - return; - } - const to = state.doc.length; - const update = state.update({ - changes: { from: 0, to, insert: doc }, - }); - leftView.dispatch(update); - }); - - // When the linter changes, update the linter - onSelectorChange(store, getLinter, (linter) => { - leftView.dispatch({ - effects: linterCompartment.reconfigure(linter), - }); - }); -} diff --git a/website/src/components/PlaygroundFeatures/editors/OutputView.tsx b/website/src/components/PlaygroundFeatures/editors/OutputView.tsx deleted file mode 100644 index 1d7bf693..00000000 --- a/website/src/components/PlaygroundFeatures/editors/OutputView.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import React from "react"; -import GraphQLOutputView from "./GraphQLOutputView"; -import CodegenOutputView from "./CodegenOutputView"; -import { getOutputOption, useAppSelector } from "../store"; - -export default function OutputView() { - const outputOption = useAppSelector(getOutputOption); - switch (outputOption) { - case "sdl": - return ; - case "typescript": - return ; - } -} diff --git a/website/src/components/PlaygroundFeatures/editors/theme.ts b/website/src/components/PlaygroundFeatures/editors/theme.ts deleted file mode 100644 index 933c99b5..00000000 --- a/website/src/components/PlaygroundFeatures/editors/theme.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { EditorView } from "@codemirror/view"; - -export const Theme = EditorView.theme({ - "&": { fontSize: "18px" }, - ".cm-gutters": { - backgroundColor: "var(--ifm-color-emphasis-100)", - color: "var(--ifm-font-color-base)", - border: "none", - borderRight: "1px solid var(--ifm-color-emphasis-300)", - }, -}); diff --git a/website/src/components/PlaygroundFeatures/gratsStoreBindings.ts b/website/src/components/PlaygroundFeatures/gratsStoreBindings.ts new file mode 100644 index 00000000..652fca45 --- /dev/null +++ b/website/src/components/PlaygroundFeatures/gratsStoreBindings.ts @@ -0,0 +1,159 @@ +import { getConfig, getDoc, getView, State } from "./store"; +import { codegen } from "grats/src/codegen"; +import * as ts from "typescript"; +import lzstring from "lz-string"; +import { + createDefaultMapFromCDN, + createSystem, + createVirtualCompilerHost, +} from "@typescript/vfs"; +import ExecutionEnvironment from "@docusaurus/ExecutionEnvironment"; +import { buildSchemaAndDocResultWithHost } from "grats/src/lib"; +import GRATS_TYPE_DECLARATIONS from "!!raw-loader!grats/src/Types.ts"; +import { createSelector } from "reselect"; +import { print } from "graphql"; +import { printSDLWithoutMetadata } from "grats/src/printSchema"; +import store from "./store"; + +const SHOULD_CACHE = true; +const GRATS_PATH = "/node_modules/grats/src/index.ts"; + +if (ExecutionEnvironment.canUseDOM) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + window.process = { + // Grats depends upon calling path.resolver and path.relative + // which depend upon process.cwd() being set. + // Here we supply a fake cwd() function that returns the root + cwd() { + return "/"; + }, + }; +} + +export async function bindGratsToStore() { + const fsMap = await createDefaultMapFromCDN( + { target: ts.ScriptTarget.ES2021, lib: ["es2021"] }, + ts.version, + SHOULD_CACHE, + ts, + lzstring, + ); + + // There's some bug/mismatch where the files that are expected by our version of + // TypeScript don't exactly match those included in/accessible by the + // `@typescript/vfs` package. If it's missing a file it crashes, so we'll + // include them as empty for now. Their actual contents are just a few very + // obscure APIs, so it's probably fine to just leave them for now. + for (const fileName of [ + "/lib.es2016.intl.d.ts", + "/lib.dom.asynciterable.d.ts", + ]) { + fsMap.set(fileName, ""); + } + + const system = createSystem(fsMap); + + store.dispatch({ type: "TS_LOADED", system, fsMap }); +} + +export const getTs = (state: State) => state.ts; + +export const getBuild = createSelector( + getTs, + getDoc, + getConfig, + (ts, doc, config) => { + if (ts == null) return null; + + return build(ts.fsMap, ts.system, doc, config); + }, +); + +export const getErrorText = createSelector(getBuild, (build) => { + if (build == null) return null; + + if (build.kind === "ERROR") { + return build.err.formatDiagnosticsWithContext(); + } + return null; +}); + +export const getDiagnostics = createSelector(getBuild, (build) => { + if (build == null) return []; + + if (build.kind === "ERROR") { + return build.err._diagnostics; + } + return []; +}); + +export const getSchemaText = createSelector( + getBuild, + getView, + (build, view) => { + if (build == null) return ""; + + if (build.kind === "OK") { + const doc = build.value.doc; + if (!view.showGratsDirectives) { + return printSDLWithoutMetadata(doc); + } + return print(doc); + } + return null; + }, +); + +export const getTsSchema = createSelector( + getBuild, + getConfig, + (build, config) => { + if (build == null) return ""; + + if (build.kind === "OK") { + return codegen(build.value.schema, config, "./schema.ts"); + } + return null; + }, +); + +function build(fsMap, system, text, config) { + fsMap.set("index.ts", text); + fsMap.set(GRATS_PATH, GRATS_TYPE_DECLARATIONS); + fsMap.set( + "/node_modules/graphql/index.ts", + ` + export type GraphQLResolveInfo = any; + `, + ); + const compilerOpts = { + allowJs: true, + baseUrl: "./", + paths: { grats: [GRATS_PATH] }, + lib: [...fsMap.keys()], + }; + const host = createVirtualCompilerHost(system, compilerOpts, ts); + + const parsedOptions = { + raw: { + grats: config, + }, + options: compilerOpts, + fileNames: ["index.ts"], + errors: [], + }; + try { + return buildSchemaAndDocResultWithHost(parsedOptions, host.compilerHost); + } catch (e) { + const message = `Grats playground bug encountered. Please report this error:\n\n ${e.stack}`; + throw e; + return { + kind: "ERROR", + err: { + formatDiagnosticsWithContext: () => message, + _diagnostics: [] as ts.Diagnostic[], + }, + }; + } +} diff --git a/website/src/components/PlaygroundFeatures/index.module.css b/website/src/components/PlaygroundFeatures/index.module.css index b502a245..74181a93 100644 --- a/website/src/components/PlaygroundFeatures/index.module.css +++ b/website/src/components/PlaygroundFeatures/index.module.css @@ -9,14 +9,6 @@ body { margin: 0; } -.cm-editor { - height: 100%; - width: 100%; -} -.cm-scroller { - overflow: auto; -} - #format-button { background-color: #eee; border: 1px solid #ccc; diff --git a/website/src/components/PlaygroundFeatures/linter.ts b/website/src/components/PlaygroundFeatures/linter.ts deleted file mode 100644 index 11f53b0b..00000000 --- a/website/src/components/PlaygroundFeatures/linter.ts +++ /dev/null @@ -1,181 +0,0 @@ -import { createSystem, createVirtualCompilerHost } from "@typescript/vfs"; -import * as ts from "typescript"; -import { buildSchemaAndDocResultWithHost, GratsConfig } from "grats/src/lib"; -import { codegen } from "grats/src/codegen"; -import { ReportableDiagnostics } from "grats/src/utils/DiagnosticError"; -import { printSDLWithoutMetadata } from "grats/src/printSchema"; -import { linter } from "@codemirror/lint"; -import { DocumentNode, GraphQLSchema, print } from "graphql"; -import GRATS_TYPE_DECLARATIONS from "!!raw-loader!grats/src/Types.ts"; -import ExecutionEnvironment from "@docusaurus/ExecutionEnvironment"; - -import store from "./store"; - -const GRATS_PATH = "/node_modules/grats/src/index.ts"; - -if (ExecutionEnvironment.canUseDOM) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - window.process = { - // Grats depends upon calling path.resolver and path.relative - // which depend upon process.cwd() being set. - // Here we supply a fake cwd() function that returns the root - cwd() { - return "/"; - }, - }; -} - -function buildSchemaResultWithFsMap( - fsMap: Map, - text: string, - config: GratsConfig, -) { - fsMap.set("index.ts", text); - fsMap.set(GRATS_PATH, GRATS_TYPE_DECLARATIONS); - fsMap.set( - "/node_modules/graphql/index.ts", - ` - export type GraphQLResolveInfo = any; - `, - ); - // fsMap.set(GRATS_PACKAGE_JSON_PATH, GRATS_PACKAGE_JSON); - // TODO: Don't recreate the system each time! - const system = createSystem(fsMap); - - const compilerOpts = { - allowJs: true, - baseUrl: "./", - paths: { grats: [GRATS_PATH] }, - lib: [...fsMap.keys()], - }; - const host = createVirtualCompilerHost(system, compilerOpts, ts); - - const parsedOptions = { - raw: { - grats: config, - }, - options: compilerOpts, - fileNames: ["index.ts"], - errors: [], - }; - - try { - return buildSchemaAndDocResultWithHost(parsedOptions, host.compilerHost); - } catch (e) { - const message = `Grats playground bug encountered. Please report this error:\n\n ${e.stack}`; - throw e; - return { - kind: "ERROR", - err: { - formatDiagnosticsWithContext: () => message, - _diagnostics: [] as ReportableDiagnostics[], - }, - }; - } -} - -export function createLinter( - fsMap: Map, - view, - config: GratsConfig, -) { - return linter((codeMirrorView) => { - const text = codeMirrorView.viewState.state.doc.toString(); - - const result = buildSchemaResultWithFsMap(fsMap, text, config); - - store.dispatch({ type: "NEW_DOCUMENT_TEXT", value: text }); - - if (result.kind === "ERROR") { - const errorText = result.err.formatDiagnosticsWithContext(); - const output = `# ERROR MESSAGE\n# =============\n\n${commentLines( - errorText, - )}`; - store.dispatch({ - type: "GRATS_EMITTED_NEW_RESULT", - graphql: output, - typescript: output, - }); - - return result.err._diagnostics - .filter((diagnostic) => { - if (diagnostic.file == null) { - return false; - } - return diagnostic.file.fileName === "index.ts"; - }) - .map((diagnostic) => { - const actions = []; - if (diagnostic.fix) { - const action = gratsFixToCodeMirrorAction(diagnostic.fix); - actions.push(action); - } - return { - from: diagnostic.start, - to: diagnostic.start + diagnostic.length, - severity: "error", - message: diagnostic.messageText, - actions, - }; - }); - } - - const codegenOutput = computeCodegenOutput(result.value.schema, config); - const output = computeOutput(result.value.doc, view); - - store.dispatch({ - type: "GRATS_EMITTED_NEW_RESULT", - graphql: output, - typescript: codegenOutput, - }); - - return []; - }); -} - -function computeOutput( - doc: DocumentNode, - view: { showGratsDirectives: boolean }, -): string { - if (!view.showGratsDirectives) { - return printSDLWithoutMetadata(doc); - } - return print(doc); -} - -function computeCodegenOutput( - schema: GraphQLSchema, - config: GratsConfig, -): string { - return codegen(schema, config, "./schema.ts"); -} - -function commentLines(text: string): string { - return text - .split("\n") - .map((line) => `# ${line}`) - .join("\n"); -} - -function gratsFixToCodeMirrorAction(fix) { - const changes = []; - for (const tsChange of fix.changes) { - for (const textChange of tsChange.textChanges) { - changes.push({ - from: textChange.span.start, - to: textChange.span.start + textChange.span.length, - insert: textChange.newText, - }); - } - } - if (changes.length === 0) { - return null; - } - return { - name: fix.description, - apply: (view) => { - view.dispatch({ changes }); - }, - }; -} diff --git a/website/src/components/PlaygroundFeatures/store.ts b/website/src/components/PlaygroundFeatures/store.ts index bebf36bf..98207b02 100644 --- a/website/src/components/PlaygroundFeatures/store.ts +++ b/website/src/components/PlaygroundFeatures/store.ts @@ -9,6 +9,7 @@ export type State = { doc: string; config: { nullableByDefault: boolean; + strictSemanticNullability: boolean; reportTypeScriptTypeErrors: boolean; }; view: { @@ -19,6 +20,10 @@ export type State = { graphql: string; typescript: string; }; + ts: { + system: any; + fsMap: Map; + } | null; VERSION: number; }; @@ -38,6 +43,10 @@ export type Action = type: "DEFAULT_NULLABLE_INPUT_CHANGED"; value: boolean; } + | { + type: "SEMANTIC_NULLABILITY_INPUT_CHANGED"; + value: boolean; + } | { type: "GRATS_EMITTED_NEW_RESULT"; graphql: string; @@ -50,6 +59,11 @@ export type Action = | { type: "OUTPUT_VIEW_SELECTION_CHANGED"; value: "sdl" | "typescript"; + } + | { + type: "TS_LOADED"; + system: any; + fsMap: Map; }; function reducer(state: State = stateFromUrl(), action: Action) { @@ -75,14 +89,32 @@ function reducer(state: State = stateFromUrl(), action: Action) { outputOption: action.value, }, }; - case "DEFAULT_NULLABLE_INPUT_CHANGED": + case "DEFAULT_NULLABLE_INPUT_CHANGED": { + const strictSemanticNullability = action.value + ? state.config.strictSemanticNullability + : false; return { ...state, config: { ...state.config, + strictSemanticNullability, nullableByDefault: action.value, }, }; + } + case "SEMANTIC_NULLABILITY_INPUT_CHANGED": { + const nullableByDefault = action.value + ? true + : state.config.nullableByDefault; + return { + ...state, + config: { + ...state.config, + nullableByDefault, + strictSemanticNullability: action.value, + }, + }; + } case "GRATS_EMITTED_NEW_RESULT": return { ...state, @@ -97,6 +129,15 @@ function reducer(state: State = stateFromUrl(), action: Action) { doc: action.value, }; } + case "TS_LOADED": { + return { + ...state, + ts: { + system: action.system, + fsMap: action.fsMap, + }, + }; + } default: { const _: never = action; } @@ -158,6 +199,10 @@ export function getNullableByDefault(state): boolean { return state.config.nullableByDefault; } +export function getSemanticNullability(state): boolean { + return state.config.strictSemanticNullability; +} + export function getShowGratsDirectives(state): boolean { return state.view.showGratsDirectives; } @@ -190,7 +235,7 @@ export type SerializableState = { // TODO: Avoid recomputing export function getSerializabelState(state: State): SerializableState { - const { gratsResult, ...serializableState } = state; + const { gratsResult, ts, ...serializableState } = state; return serializableState; } diff --git a/website/src/components/logo.tsx b/website/src/components/logo.tsx index 2913e2b1..cab2a4be 100644 --- a/website/src/components/logo.tsx +++ b/website/src/components/logo.tsx @@ -5,38 +5,51 @@ const _TS_DARK = "#00273F"; const GRAPHQL_RHODAMINE = "#E10098"; const GRAPHQL_MID = "rgb(196 56 150)"; -const OFFSET = 20; +export const OFFSET = 20; -function calculateHexagonPoints(): { x: number; y: number }[] { - const width = 100; - const height = 100; - - const centerX = width / 2; - const centerY = height / 2; - - const radius = height / 2; - - const points: { x: number; y: number }[] = []; - - // Calculate the angle offset (30 degrees in radians) - const angleOffset = Math.PI / 6; - - // Calculate the points of the hexagon - for (let i = 0; i < 6; i++) { - const angle = (i * Math.PI) / 3 + angleOffset; - const x = centerX + radius * Math.cos(angle); - const y = centerY + radius * Math.sin(angle); - points.push({ x, y }); - } - - return points; +export function GratsLogoWithName() { + return ( + + + + + + Grats + + + ); } -function offset(pos) { - return { x: pos.x + OFFSET, y: pos.y + OFFSET }; +export function GratsLogo() { + return ( + + + + ); } -export function GratsLogo({ opacity = 1 }) { +export function GratsLogoImpl({ opacity = 1 }) { // Compute points of the hexagon with points // at the middle top and bottom const points = calculateHexagonPoints().map(offset); @@ -58,11 +71,7 @@ export function GratsLogo({ opacity = 1 }) { const gradient0 = "grad0"; const gradient45 = "grad45"; return ( - + <> - + ); } @@ -167,3 +176,32 @@ function LinearGradient({ angle, startColor, endColor, id }) { ); } + +function calculateHexagonPoints(): { x: number; y: number }[] { + const width = 100; + const height = 100; + + const centerX = width / 2; + const centerY = height / 2; + + const radius = height / 2; + + const points: { x: number; y: number }[] = []; + + // Calculate the angle offset (30 degrees in radians) + const angleOffset = Math.PI / 6; + + // Calculate the points of the hexagon + for (let i = 0; i < 6; i++) { + const angle = (i * Math.PI) / 3 + angleOffset; + const x = centerX + radius * Math.cos(angle); + const y = centerY + radius * Math.sin(angle); + points.push({ x, y }); + } + + return points; +} + +function offset(pos) { + return { x: pos.x + OFFSET, y: pos.y + OFFSET }; +} diff --git a/website/src/pages/logo/index.tsx b/website/src/pages/logo/index.tsx new file mode 100644 index 00000000..c3424ea9 --- /dev/null +++ b/website/src/pages/logo/index.tsx @@ -0,0 +1,5 @@ +import { GratsLogoWithName } from "../../components/logo"; + +export default function LogoPage() { + return ; +} diff --git a/website/src/pages/playground/index.tsx b/website/src/pages/playground/index.tsx index 68d6be39..40df7f13 100644 --- a/website/src/pages/playground/index.tsx +++ b/website/src/pages/playground/index.tsx @@ -1,13 +1,22 @@ -import React, { useLayoutEffect, useState } from "react"; -import InputView from "../../components/PlaygroundFeatures/editors/InputView"; -import OutputView from "../../components/PlaygroundFeatures/editors/OutputView"; -import ConfigBar from "../../components/PlaygroundFeatures/ConfigBar"; import Layout from "@theme/Layout"; -import store from "../../components/PlaygroundFeatures/store"; +import React, { Suspense } from "react"; +import ConfigBar from "../../components/PlaygroundFeatures/ConfigBar"; +import store, { useUrlState } from "../../components/PlaygroundFeatures/store"; import { Provider } from "react-redux"; import BrowserOnly from "@docusaurus/BrowserOnly"; +import FillRemainingHeight from "../../components/PlaygroundFeatures/FillRemainingHeight"; +import { bindGratsToStore } from "../../components/PlaygroundFeatures/gratsStoreBindings"; +import ExecutionEnvironment from "@docusaurus/ExecutionEnvironment"; +const BothEditors = React.lazy( + () => import("../../components/MonacoPlayground/BothEditors"), +); + +if (ExecutionEnvironment.canUseDOM) { + bindGratsToStore(); +} -export default function Playground(): JSX.Element { +export default function EditorView() { + useUrlState(store); return ( @@ -34,8 +43,30 @@ export default function Playground(): JSX.Element { overflow: "scroll", }} > - - + +
+ Loading... +
+
+ } + > + + @@ -45,35 +76,3 @@ export default function Playground(): JSX.Element { ); } - -// On mount, measures the window height and current vertical offset, and -// renders children into a div that stretches to the bottom of the viewport. -function FillRemainingHeight({ children, minHeight }) { - const [containerRef, setContainerRef] = useState(null); - const [height, setHeight] = useState(null); - useLayoutEffect(() => { - if (containerRef == null) { - return; - } - - function updateSize() { - const verticalOffset = containerRef.getBoundingClientRect().y; - const available = Math.max( - window.innerHeight - verticalOffset, - minHeight, - ); - setHeight(available); - } - - updateSize(); - - window.addEventListener("resize", updateSize); - return () => window.removeEventListener("resize", updateSize); - }, [containerRef, minHeight]); - - return ( -
- {height != null && children} -
- ); -}