diff --git a/.gitignore b/.gitignore index a7a99033e4..972b88d8e1 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ TODO /gh-pages /npm /dist +/coverage \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index c8a9f2df0b..163ed16800 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,11 @@ "version": "5.1.1", "license": "MIT", "devDependencies": { + "@codemirror/commands": "^6.8.1", + "@codemirror/lang-javascript": "^6.2.3", + "@codemirror/state": "^6.5.2", + "@codemirror/theme-one-dark": "^6.1.2", + "@codemirror/view": "^6.36.5", "@eslint/js": "^9.20.0", "@jest/globals": "^29.7.0", "@rollup/plugin-buble": "1.0.3", @@ -22,6 +27,7 @@ "@types/random-seed": "0.3.5", "@types/react": "17.0.11", "benchmark": "2.1.4", + "codemirror": "^6.0.1", "colors": "1.4.0", "cpy-cli": "^5.0.0", "eslint": "^9.20.1", @@ -34,6 +40,7 @@ "globals": "^15.15.0", "jest": "^29.0.0", "jest-environment-jsdom": "^29.6.4", + "jsonml-html": "^1.1.0", "make-synchronous": "0.1.1", "marked": "^11.2.0", "marked-highlight": "^2.1.0", @@ -580,6 +587,122 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@codemirror/autocomplete": { + "version": "6.18.6", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.6.tgz", + "integrity": "sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@codemirror/commands": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.8.1.tgz", + "integrity": "sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.4.0", + "@codemirror/view": "^6.27.0", + "@lezer/common": "^1.1.0" + } + }, + "node_modules/@codemirror/lang-javascript": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.3.tgz", + "integrity": "sha512-8PR3vIWg7pSu7ur8A07pGiYHgy3hHj+mRYRCSG8q+mPIrl0F02rgpGv+DsQTHRTc30rydOsf5PZ7yjKFg2Ackw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.6.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/javascript": "^1.0.0" + } + }, + "node_modules/@codemirror/language": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.0.tgz", + "integrity": "sha512-A7+f++LodNNc1wGgoRDTt78cOwWm9KVezApgjOMp1W4hM0898nsqBXwF+sbePE7ZRcjN7Sa1Z5m2oN27XkmEjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.23.0", + "@lezer/common": "^1.1.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" + } + }, + "node_modules/@codemirror/lint": { + "version": "6.8.5", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.5.tgz", + "integrity": "sha512-s3n3KisH7dx3vsoeGMxsbRAgKe4O1vbrnKBClm99PU0fWxmxsx5rR2PfqQgIt+2MMJBHbiJ5rfIdLYfB9NNvsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.35.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/search": { + "version": "6.5.10", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.10.tgz", + "integrity": "sha512-RMdPdmsrUf53pb2VwflKGHEe1XVM07hI7vV2ntgw1dmqhimpatSJKva4VA9h4TLUDOD4EIF02201oZurpnEFsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/state": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz", + "integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@marijn/find-cluster-break": "^1.0.0" + } + }, + "node_modules/@codemirror/theme-one-dark": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.2.tgz", + "integrity": "sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/highlight": "^1.0.0" + } + }, + "node_modules/@codemirror/view": { + "version": "6.36.5", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.36.5.tgz", + "integrity": "sha512-cd+FZEUlu3GQCYnguYm3EkhJ8KJVisqqUsCOKedBoAt/d9c76JUUap6U0UrpElln5k6VyrEOYliMuDAKIeDQLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.5.0", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, "node_modules/@corex/deepmerge": { "version": "4.0.43", "resolved": "https://registry.npmjs.org/@corex/deepmerge/-/deepmerge-4.0.43.tgz", @@ -1793,6 +1916,52 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@lezer/common": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz", + "integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@lezer/highlight": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.1.tgz", + "integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/javascript": { + "version": "1.4.21", + "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.21.tgz", + "integrity": "sha512-lL+1fcuxWYPURMM/oFZLEDm0XuLN128QPV+VuGtKpeaOGdcl9F2LYC3nh1S9LkPqx9M0mndZFdXCipNAZpzIkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.1.3", + "@lezer/lr": "^1.3.0" + } + }, + "node_modules/@lezer/lr": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz", + "integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@marijn/find-cluster-break": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", + "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", + "dev": true, + "license": "MIT" + }, "node_modules/@next/env": { "version": "14.2.26", "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.26.tgz", @@ -4105,6 +4274,22 @@ "node": ">= 0.12.0" } }, + "node_modules/codemirror": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz", + "integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/commands": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/search": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + } + }, "node_modules/collect-v8-coverage": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", @@ -4275,6 +4460,13 @@ "dev": true, "license": "MIT" }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", + "dev": true, + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -7804,6 +7996,13 @@ "node": ">=6" } }, + "node_modules/jsonml-html": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsonml-html/-/jsonml-html-1.1.0.tgz", + "integrity": "sha512-L3aCxuWRalEKY/8krm1gDhjq9yTfmzoBaoiULgnMY+mAamCiKfeV/ilLcMv+bxdzb8ZgI9VjaSPBwrTTxgpF6Q==", + "dev": true, + "license": "ISC" + }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -10179,6 +10378,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/style-mod": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", + "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==", + "dev": true, + "license": "MIT" + }, "node_modules/styled-jsx": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", @@ -10820,6 +11026,13 @@ "spdx-expression-parse": "^3.0.0" } }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "dev": true, + "license": "MIT" + }, "node_modules/w3c-xmlserializer": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", @@ -11517,6 +11730,113 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "@codemirror/autocomplete": { + "version": "6.18.6", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.6.tgz", + "integrity": "sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==", + "dev": true, + "requires": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0" + } + }, + "@codemirror/commands": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.8.1.tgz", + "integrity": "sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw==", + "dev": true, + "requires": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.4.0", + "@codemirror/view": "^6.27.0", + "@lezer/common": "^1.1.0" + } + }, + "@codemirror/lang-javascript": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.3.tgz", + "integrity": "sha512-8PR3vIWg7pSu7ur8A07pGiYHgy3hHj+mRYRCSG8q+mPIrl0F02rgpGv+DsQTHRTc30rydOsf5PZ7yjKFg2Ackw==", + "dev": true, + "requires": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.6.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/javascript": "^1.0.0" + } + }, + "@codemirror/language": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.0.tgz", + "integrity": "sha512-A7+f++LodNNc1wGgoRDTt78cOwWm9KVezApgjOMp1W4hM0898nsqBXwF+sbePE7ZRcjN7Sa1Z5m2oN27XkmEjQ==", + "dev": true, + "requires": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.23.0", + "@lezer/common": "^1.1.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" + } + }, + "@codemirror/lint": { + "version": "6.8.5", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.5.tgz", + "integrity": "sha512-s3n3KisH7dx3vsoeGMxsbRAgKe4O1vbrnKBClm99PU0fWxmxsx5rR2PfqQgIt+2MMJBHbiJ5rfIdLYfB9NNvsA==", + "dev": true, + "requires": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.35.0", + "crelt": "^1.0.5" + } + }, + "@codemirror/search": { + "version": "6.5.10", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.10.tgz", + "integrity": "sha512-RMdPdmsrUf53pb2VwflKGHEe1XVM07hI7vV2ntgw1dmqhimpatSJKva4VA9h4TLUDOD4EIF02201oZurpnEFsg==", + "dev": true, + "requires": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "crelt": "^1.0.5" + } + }, + "@codemirror/state": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz", + "integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==", + "dev": true, + "requires": { + "@marijn/find-cluster-break": "^1.0.0" + } + }, + "@codemirror/theme-one-dark": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.2.tgz", + "integrity": "sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==", + "dev": true, + "requires": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/highlight": "^1.0.0" + } + }, + "@codemirror/view": { + "version": "6.36.5", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.36.5.tgz", + "integrity": "sha512-cd+FZEUlu3GQCYnguYm3EkhJ8KJVisqqUsCOKedBoAt/d9c76JUUap6U0UrpElln5k6VyrEOYliMuDAKIeDQLg==", + "dev": true, + "requires": { + "@codemirror/state": "^6.5.0", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, "@corex/deepmerge": { "version": "4.0.43", "resolved": "https://registry.npmjs.org/@corex/deepmerge/-/deepmerge-4.0.43.tgz", @@ -12265,6 +12585,47 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "@lezer/common": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz", + "integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==", + "dev": true + }, + "@lezer/highlight": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.1.tgz", + "integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==", + "dev": true, + "requires": { + "@lezer/common": "^1.0.0" + } + }, + "@lezer/javascript": { + "version": "1.4.21", + "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.21.tgz", + "integrity": "sha512-lL+1fcuxWYPURMM/oFZLEDm0XuLN128QPV+VuGtKpeaOGdcl9F2LYC3nh1S9LkPqx9M0mndZFdXCipNAZpzIkQ==", + "dev": true, + "requires": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.1.3", + "@lezer/lr": "^1.3.0" + } + }, + "@lezer/lr": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz", + "integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==", + "dev": true, + "requires": { + "@lezer/common": "^1.0.0" + } + }, + "@marijn/find-cluster-break": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", + "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", + "dev": true + }, "@next/env": { "version": "14.2.26", "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.26.tgz", @@ -13763,6 +14124,21 @@ "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", "dev": true }, + "codemirror": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz", + "integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==", + "dev": true, + "requires": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/commands": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/search": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + } + }, "collect-v8-coverage": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", @@ -13887,6 +14263,12 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, + "crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", + "dev": true + }, "cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -16322,6 +16704,12 @@ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, + "jsonml-html": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsonml-html/-/jsonml-html-1.1.0.tgz", + "integrity": "sha512-L3aCxuWRalEKY/8krm1gDhjq9yTfmzoBaoiULgnMY+mAamCiKfeV/ilLcMv+bxdzb8ZgI9VjaSPBwrTTxgpF6Q==", + "dev": true + }, "jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -17951,6 +18339,12 @@ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, + "style-mod": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", + "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==", + "dev": true + }, "styled-jsx": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", @@ -18359,6 +18753,12 @@ "spdx-expression-parse": "^3.0.0" } }, + "w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "dev": true + }, "w3c-xmlserializer": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", diff --git a/package.json b/package.json index 81a29d64cb..d96af9c361 100644 --- a/package.json +++ b/package.json @@ -83,6 +83,11 @@ "trailingComma": "es5" }, "devDependencies": { + "@codemirror/commands": "^6.8.1", + "@codemirror/lang-javascript": "^6.2.3", + "@codemirror/state": "^6.5.2", + "@codemirror/theme-one-dark": "^6.1.2", + "@codemirror/view": "^6.36.5", "@eslint/js": "^9.20.0", "@jest/globals": "^29.7.0", "@rollup/plugin-buble": "1.0.3", @@ -96,6 +101,7 @@ "@types/random-seed": "0.3.5", "@types/react": "17.0.11", "benchmark": "2.1.4", + "codemirror": "^6.0.1", "colors": "1.4.0", "cpy-cli": "^5.0.0", "eslint": "^9.20.1", @@ -108,6 +114,7 @@ "globals": "^15.15.0", "jest": "^29.0.0", "jest-environment-jsdom": "^29.6.4", + "jsonml-html": "^1.1.0", "make-synchronous": "0.1.1", "marked": "^11.2.0", "marked-highlight": "^2.1.0", diff --git a/website/src/Header.tsx b/website/src/Header.tsx index e2ba1584ad..b8e8b67d9e 100644 --- a/website/src/Header.tsx +++ b/website/src/Header.tsx @@ -109,6 +109,7 @@ export function HeaderLinks({ return (
+ Playground ({ version }))]; -} - -export default function Test({ params }: { params: { version: string } }) { - const version = getVersionFromParams(params); - const defs = getTypeDefs(version); - const sidebarLinks = getSidebarLinks(defs); - - return ( - <> - - -
-

Construction

- -
-

- List() -

-
-
-

- Create a new immutable List containing the values of the - provided collection-like. -

-
- - - -
-

Discussion

-
-

- Note:{' '} - - List - {' '} - is a factory function and not a class, and does not use the - - new - {' '} - keyword during construction. -

-
-
- - -
- -

Sandpack !

-
-

- push() -

-
-
-

- Returns a new List with the provided{' '} - - values - {' '} - appended, starting at this List's{' '} - - size - - . -

-
-
-
- - -
-
- - ); -} diff --git a/website/src/app/play/layout.tsx b/website/src/app/play/layout.tsx new file mode 100644 index 0000000000..4f7b7c3a3c --- /dev/null +++ b/website/src/app/play/layout.tsx @@ -0,0 +1,21 @@ +import { DocHeader } from '../../DocHeader'; +import { ImmutableConsole } from '../../ImmutableConsole'; +import { getVersions } from '../../static/getVersions'; + +export default function VersionLayout({ + children, +}: { + children: React.ReactNode; +}) { + const versions = getVersions(); + + return ( +
+ + +
+
{children}
+
+
+ ); +} diff --git a/website/src/app/play/page.tsx b/website/src/app/play/page.tsx new file mode 100644 index 0000000000..94fda0a6e9 --- /dev/null +++ b/website/src/app/play/page.tsx @@ -0,0 +1,46 @@ +import { Metadata } from 'next'; +import { getVersions } from '../../static/getVersions'; +import { getTypeDefs } from '../../static/getTypeDefs'; +import { DocSearch } from '../../DocSearch'; +import { SideBar } from '../../Sidebar'; +import { getSidebarLinks } from '../../getSidebarLinks'; +import dynamic from 'next/dynamic'; + +export async function generateStaticParams() { + return [...getVersions().map((version) => ({ version }))]; +} + +export async function generateMetadata(): Promise { + return { + title: `Playground — Immutable.js`, + }; +} + +const ReplNoSSR = dynamic(() => import('../../repl/Repl'), { ssr: false }); + +export default function OverviewDocPage() { + const versions = getVersions(); + const version = versions[0]; + const defs = getTypeDefs(version); + + const sidebarLinks = getSidebarLinks(defs); + + return ( + <> + +
+ +

Playgroud ({version})

+ + str.charAt(0).toUpperCase() + str.slice(1); + +List(['apple', 'banana', 'coconut']) + .push('dragonfruit') + .map((fruit) => upperFirst(fruit)) +`} + /> +
+ + ); +} diff --git a/website/src/repl/Editor.tsx b/website/src/repl/Editor.tsx new file mode 100644 index 0000000000..e284e14f50 --- /dev/null +++ b/website/src/repl/Editor.tsx @@ -0,0 +1,55 @@ +import { useEffect, useRef } from 'react'; +import { basicSetup } from 'codemirror'; +import { EditorView, keymap } from '@codemirror/view'; +import { defaultKeymap, indentWithTab } from '@codemirror/commands'; +import { EditorState } from '@codemirror/state'; +import { javascript } from '@codemirror/lang-javascript'; +// TODO activate this when we have a dark mode +// import { oneDark } from '@codemirror/theme-one-dark'; + +type Props = { + value: string; + onChange: (value: string) => void; +}; + +export function Editor({ value, onChange }: Props): JSX.Element { + const editor = useRef(null); + + const onUpdate = EditorView.updateListener.of((v) => { + onChange(v.state.doc.toString()); + }); + + useEffect(() => { + if (!editor.current) { + return; + } + const startState = EditorState.create({ + doc: value, + extensions: [ + basicSetup, + keymap.of([...defaultKeymap, indentWithTab]), + javascript(), + // TODO activate this when we have a dark mode + // oneDark, + + onUpdate, + ], + }); + + const view = new EditorView({ + state: startState, + parent: editor.current, + }); + + return () => { + view.destroy(); + }; + }, []); + + return ( +
+ ); +} diff --git a/website/src/repl/FormatterOutput.tsx b/website/src/repl/FormatterOutput.tsx new file mode 100644 index 0000000000..fd0a9b2e2b --- /dev/null +++ b/website/src/repl/FormatterOutput.tsx @@ -0,0 +1,43 @@ +import { toHTML } from 'jsonml-html'; +import { useEffect, useRef } from 'react'; + +/** + * immutable-devtools is a console custom formatter. + * Console formatters does use jsonml format. + * {@see https://firefox-source-docs.mozilla.org/devtools-user/custom_formatters/index.html} for a documentation from the Firefox team. + * The `jsonml-html` package can convert jsonml to HTML. + */ +type Props = { + output: { + header: Array; + body?: Array; + }; +}; + +export default function FormatterOutput({ output }: Props): JSX.Element { + const header = useRef(null); + const body = useRef(null); + + const htmlHeader = toHTML(output.header); + + useEffect(() => { + if (header.current && htmlHeader) { + header.current.replaceChildren(htmlHeader); + } + }, [htmlHeader]); + + const htmlBody = output.body ? toHTML(output.body) : null; + + useEffect(() => { + if (body.current) { + body.current.replaceChildren(htmlBody ?? ''); + } + }, [htmlBody]); + + return ( + <> +
+
+ + ); +} diff --git a/website/src/repl/Repl.tsx b/website/src/repl/Repl.tsx new file mode 100644 index 0000000000..b37a5df69f --- /dev/null +++ b/website/src/repl/Repl.tsx @@ -0,0 +1,159 @@ +'use client'; +import React, { useEffect, useRef, useState } from 'react'; +import { Editor } from './Editor'; +import FormatterOutput from './FormatterOutput'; +import './repl.css'; + +type Props = { defaultValue: string }; + +function Repl({ defaultValue }: Props): JSX.Element { + const [code, setCode] = useState(defaultValue); + const [output, setOutput] = useState<{ + header: Array; + body?: Array; + }>({ header: [] }); + const workerRef = useRef(null); + + useEffect(() => { + const workerScript = ` + importScripts('https://cdn.jsdelivr.net/npm/immutable@5.1.1', 'https://cdn.jsdelivr.net/npm/immutable-devtools@0.1.5'); + + // extract all Immutable exports to have them available in the worker automatically + const { + version, + Collection, + Iterable, + Seq, + Map, + OrderedMap, + List, + Stack, + Set, + OrderedSet, + PairSorting, + Record, + Range, + Repeat, + is, + fromJS, + hash, + isImmutable, + isCollection, + isKeyed, + isIndexed, + isAssociative, + isOrdered, + isPlainObject, + isValueObject, + isSeq, + isList, + isMap, + isOrderedMap, + isStack, + isSet, + isOrderedSet, + isRecord, + get, + getIn, + has, + hasIn, + merge, + mergeDeep, + mergeWith, + mergeDeepWith, + remove, + removeIn, + set, + setIn, + update, + updateIn, + } = Immutable; + + immutableDevTools(Immutable); + + // hack to get the formatters from immutable-devtools as they are not exported, but they modify the "global" variable + const immutableFormaters = globalThis.devtoolsFormatters; + + // console.log(immutableFormaters) + + function normalizeResult(result) { + const formatter = immutableFormaters.find((formatter) => formatter.header(result)); + + if (!formatter) { + return undefined; + } + + return { + header: formatter.header(result), + body: formatter.hasBody(result) ? formatter.body(result) : undefined, + } + } + + self.onmessage = function(event) { + let timeoutId = setTimeout(() => { + self.postMessage({ error: "Execution timed out" }); + self.close(); + }, 2000); + + try { + const result = eval(event.data); + clearTimeout(timeoutId); + + self.postMessage({ output: normalizeResult(result) }); + } catch (error) { + console.log(error); + clearTimeout(timeoutId); + self.postMessage({ error: String(error) }); + } + }; + `; + + const workerBlob = new Blob([workerScript], { + type: 'application/javascript', + }); + workerRef.current = new Worker(URL.createObjectURL(workerBlob)); + + return () => { + workerRef.current?.terminate(); + }; + }, []); + + useEffect(() => { + runCode(); + }, []); + + const runCode = () => { + if (workerRef.current) { + workerRef.current.postMessage(code); + workerRef.current.onmessage = (event) => { + if (event.data.error) { + setOutput({ header: ['div', 'Error: ' + event.data.error] }); + } else { + setOutput(event.data.output); + } + }; + } + }; + + return ( +
+

Live example

+ +
+
+ +
+ + +
+ +
+        
+      
+
+ ); +} + +export default Repl; diff --git a/website/src/repl/repl.css b/website/src/repl/repl.css new file mode 100644 index 0000000000..986c25e362 --- /dev/null +++ b/website/src/repl/repl.css @@ -0,0 +1,40 @@ +.repl-editor-container { + display: flex; + align-items: flex-start; + gap: 8px; +} + +textarea { + width: 100%; + height: 100px; + margin-bottom: 10px; + padding: 10px; + font-family: monospace; + font-size: 14px; + border: 1px solid #ccc; + border-radius: 4px; + resize: none; +} + +button { + padding: 10px 15px; + font-size: 14px; + background-color: var(--link-color); + color: white; + border: none; + border-radius: 4px; + cursor: pointer; +} + +button:hover { + background-color: var(--link-hover-color); +} + +pre { + background-color: var(--code-block-bg-color); + padding: 10px; + border: 1px solid #ccc; + border-radius: 4px; + white-space: pre-wrap; + word-wrap: break-word; +} diff --git a/website/styles/globals.css b/website/styles/globals.css index 56158e25a5..cd810eb202 100644 --- a/website/styles/globals.css +++ b/website/styles/globals.css @@ -3,6 +3,7 @@ html, body { --link-color: #4183c4; + --link-hover-color: #2b6db0; --header-color: #212325; --header-bg-color: #6dbcdb; --body-color: #626466; @@ -57,6 +58,10 @@ a { text-decoration: none; } +a:hover { + color: var(--link-hover-color); +} + pre, code { font-family: 'Fira Mono', Menlo, monospace;