From 8dc284390cb2e2c5fb4ddc1f1cef8af568c27ae1 Mon Sep 17 00:00:00 2001 From: Petyo Ivanov Date: Fri, 19 Apr 2024 11:52:54 +0300 Subject: [PATCH] feat: use vanilla codemirror for the codemirror plugin BREAKING CHANGE: the plugin no longer accepts the sandpack theme option. Pass a codemirror 6 extension instead. --- package-lock.json | 367 ++++++++++++++++++-- package.json | 1 + src/examples/assets/code-blocks.md | 2 +- src/examples/basics.tsx | 6 +- src/plugins/codeblock/CodeBlockNode.tsx | 52 ++- src/plugins/codemirror/CodeMirrorEditor.tsx | 84 +++-- src/plugins/codemirror/index.tsx | 36 +- src/plugins/sandpack/useCodeMirrorRef.ts | 4 +- src/styles/ui.module.css | 12 +- 9 files changed, 462 insertions(+), 102 deletions(-) diff --git a/package-lock.json b/package-lock.json index d694a728..13eec5a1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "MIT", "dependencies": { "@codemirror/lang-markdown": "^6.2.3", + "@codemirror/language-data": "^6.5.1", "@codemirror/merge": "^6.4.0", "@codemirror/state": "^6.4.0", "@codemirror/view": "^6.23.0", @@ -813,6 +814,28 @@ "@lezer/common": "^1.0.0" } }, + "node_modules/@codemirror/lang-angular": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@codemirror/lang-angular/-/lang-angular-0.1.3.tgz", + "integrity": "sha512-xgeWGJQQl1LyStvndWtruUvb4SnBZDAu/gvFH/ZU+c0W25tQR8e5hq7WTwiIY2dNxnf+49mRiGI/9yxIwB6f5w==", + "dependencies": { + "@codemirror/lang-html": "^6.0.0", + "@codemirror/lang-javascript": "^6.1.2", + "@codemirror/language": "^6.0.0", + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.3.3" + } + }, + "node_modules/@codemirror/lang-cpp": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-cpp/-/lang-cpp-6.0.2.tgz", + "integrity": "sha512-6oYEYUKHvrnacXxWxYa6t4puTlbN3dgV662BDfSH8+MfjQjVmP697/KYTDOqpxgerkvoNm7q5wlFMBeX8ZMocg==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@lezer/cpp": "^1.0.0" + } + }, "node_modules/@codemirror/lang-css": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.2.0.tgz", @@ -825,6 +848,18 @@ "@lezer/css": "^1.0.0" } }, + "node_modules/@codemirror/lang-go": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@codemirror/lang-go/-/lang-go-6.0.0.tgz", + "integrity": "sha512-mMT4YeYdKGjnffDBOhr1ur1glee4oV/rfMe28vzazNHZkSt7vSiuHiBcgr3L/79Cl2RIjFdpQ1XMD0/T8Rx64g==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.6.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.0.0", + "@lezer/go": "^1.0.0" + } + }, "node_modules/@codemirror/lang-html": { "version": "6.4.5", "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.5.tgz", @@ -841,6 +876,15 @@ "@lezer/html": "^1.3.0" } }, + "node_modules/@codemirror/lang-java": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-java/-/lang-java-6.0.1.tgz", + "integrity": "sha512-OOnmhH67h97jHzCuFaIEspbmsT98fNdhVhmA3zCxW0cn7l8rChDhZtwiwJ/JOKXgfm4J+ELxQihxaI7bj7mJRg==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@lezer/java": "^1.0.0" + } + }, "node_modules/@codemirror/lang-javascript": { "version": "6.1.9", "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.1.9.tgz", @@ -855,6 +899,42 @@ "@lezer/javascript": "^1.0.0" } }, + "node_modules/@codemirror/lang-json": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.1.tgz", + "integrity": "sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@lezer/json": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-less": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-less/-/lang-less-6.0.2.tgz", + "integrity": "sha512-EYdQTG22V+KUUk8Qq582g7FMnCZeEHsyuOJisHRft/mQ+ZSZ2w51NupvDUHiqtsOy7It5cHLPGfHQLpMh9bqpQ==", + "dependencies": { + "@codemirror/lang-css": "^6.2.0", + "@codemirror/language": "^6.0.0", + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-liquid": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-liquid/-/lang-liquid-6.2.1.tgz", + "integrity": "sha512-J1Mratcm6JLNEiX+U2OlCDTysGuwbHD76XwuL5o5bo9soJtSbz2g6RU3vGHFyS5DC8rgVmFSzi7i6oBftm7tnA==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/lang-html": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.0.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.3.1" + } + }, "node_modules/@codemirror/lang-markdown": { "version": "6.2.3", "resolved": "https://registry.npmjs.org/@codemirror/lang-markdown/-/lang-markdown-6.2.3.tgz", @@ -869,6 +949,114 @@ "@lezer/markdown": "^1.0.0" } }, + "node_modules/@codemirror/lang-php": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-php/-/lang-php-6.0.1.tgz", + "integrity": "sha512-ublojMdw/PNWa7qdN5TMsjmqkNuTBD3k6ndZ4Z0S25SBAiweFGyY68AS3xNcIOlb6DDFDvKlinLQ40vSLqf8xA==", + "dependencies": { + "@codemirror/lang-html": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.0.0", + "@lezer/php": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-python": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/@codemirror/lang-python/-/lang-python-6.1.5.tgz", + "integrity": "sha512-hCm+8X6wrnXJCGf+QhmFu1AXkdTVG7dHy0Ly6SI1N3SRPptaMvwX6oNQonOXOMPvmcjiB0xq342KAxX3BYpijw==", + "dependencies": { + "@codemirror/autocomplete": "^6.3.2", + "@codemirror/language": "^6.8.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.2.1", + "@lezer/python": "^1.1.4" + } + }, + "node_modules/@codemirror/lang-rust": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-rust/-/lang-rust-6.0.1.tgz", + "integrity": "sha512-344EMWFBzWArHWdZn/NcgkwMvZIWUR1GEBdwG8FEp++6o6vT6KL9V7vGs2ONsKxxFUPXKI0SPcWhyYyl2zPYxQ==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@lezer/rust": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-sass": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-sass/-/lang-sass-6.0.2.tgz", + "integrity": "sha512-l/bdzIABvnTo1nzdY6U+kPAC51czYQcOErfzQ9zSm9D8GmNPD0WTW8st/CJwBTPLO8jlrbyvlSEcN20dc4iL0Q==", + "dependencies": { + "@codemirror/lang-css": "^6.2.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.0.2", + "@lezer/sass": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-sql": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/@codemirror/lang-sql/-/lang-sql-6.6.3.tgz", + "integrity": "sha512-fo5i3OD/7TmmqMtKycC4OaqfPsRxk0sKOb35g8cOtyUyyI2hfP2qXkDc7Asb6h7BiJK+MU/DYVPnQm6iNB5ZTw==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-vue": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@codemirror/lang-vue/-/lang-vue-0.1.3.tgz", + "integrity": "sha512-QSKdtYTDRhEHCfo5zOShzxCmqKJvgGrZwDQSdbvCRJ5pRLWBS7pD/8e/tH44aVQT6FKm0t6RVNoSUWHOI5vNug==", + "dependencies": { + "@codemirror/lang-html": "^6.0.0", + "@codemirror/lang-javascript": "^6.1.2", + "@codemirror/language": "^6.0.0", + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.3.1" + } + }, + "node_modules/@codemirror/lang-wast": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-wast/-/lang-wast-6.0.2.tgz", + "integrity": "sha512-Imi2KTpVGm7TKuUkqyJ5NRmeFWF7aMpNiwHnLQe0x9kmrxElndyH0K6H/gXtWwY6UshMRAhpENsgfpSwsgmC6Q==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-xml": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@codemirror/lang-xml/-/lang-xml-6.1.0.tgz", + "integrity": "sha512-3z0blhicHLfwi2UgkZYRPioSgVTo9PV5GP5ducFH6FaHy0IAJRg+ixj5gTR1gnT/glAIC8xv4w2VL1LoZfs+Jg==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.4.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.0.0", + "@lezer/xml": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-yaml": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-yaml/-/lang-yaml-6.1.1.tgz", + "integrity": "sha512-HV2NzbK9bbVnjWxwObuZh5FuPCowx51mEfoFT9y3y+M37fA3+pbxx4I7uePuygFzDsAmCTwQSc/kXh/flab4uw==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.2.0", + "@lezer/yaml": "^1.0.0" + } + }, "node_modules/@codemirror/language": { "version": "6.8.0", "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.8.0.tgz", @@ -882,6 +1070,43 @@ "style-mod": "^4.0.0" } }, + "node_modules/@codemirror/language-data": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@codemirror/language-data/-/language-data-6.5.1.tgz", + "integrity": "sha512-0sWxeUSNlBr6OmkqybUTImADFUP0M3P0IiSde4nc24bz/6jIYzqYSgkOSLS+CBIoW1vU8Q9KUWXscBXeoMVC9w==", + "dependencies": { + "@codemirror/lang-angular": "^0.1.0", + "@codemirror/lang-cpp": "^6.0.0", + "@codemirror/lang-css": "^6.0.0", + "@codemirror/lang-go": "^6.0.0", + "@codemirror/lang-html": "^6.0.0", + "@codemirror/lang-java": "^6.0.0", + "@codemirror/lang-javascript": "^6.0.0", + "@codemirror/lang-json": "^6.0.0", + "@codemirror/lang-less": "^6.0.0", + "@codemirror/lang-liquid": "^6.0.0", + "@codemirror/lang-markdown": "^6.0.0", + "@codemirror/lang-php": "^6.0.0", + "@codemirror/lang-python": "^6.0.0", + "@codemirror/lang-rust": "^6.0.0", + "@codemirror/lang-sass": "^6.0.0", + "@codemirror/lang-sql": "^6.0.0", + "@codemirror/lang-vue": "^0.1.1", + "@codemirror/lang-wast": "^6.0.0", + "@codemirror/lang-xml": "^6.0.0", + "@codemirror/lang-yaml": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/legacy-modes": "^6.4.0" + } + }, + "node_modules/@codemirror/legacy-modes": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@codemirror/legacy-modes/-/legacy-modes-6.4.0.tgz", + "integrity": "sha512-5m/K+1A6gYR0e+h/dEde7LoGimMjRtWXZFg4Lo70cc8HzjSdHe3fLwjWMR0VRl5KFT1SxalSap7uMgPKF28wBA==", + "dependencies": { + "@codemirror/language": "^6.0.0" + } + }, "node_modules/@codemirror/lint": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.4.0.tgz", @@ -2072,9 +2297,19 @@ } }, "node_modules/@lezer/common": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.0.3.tgz", - "integrity": "sha512-JH4wAXCgUOcCGNekQPLhVeUtIqjH0yPBs7vvUdSjyQama9618IOKFJwkv2kcqdhF0my8hQEgCTEJU0GIgnahvA==" + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.1.tgz", + "integrity": "sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ==" + }, + "node_modules/@lezer/cpp": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@lezer/cpp/-/cpp-1.1.2.tgz", + "integrity": "sha512-macwKtyeUO0EW86r3xWQCzOV9/CF8imJLpJlPv3sDY57cPGeUZ8gXWOWNlJr52TVByMV3PayFQCA5SHEERDmVQ==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } }, "node_modules/@lezer/css": { "version": "1.1.3", @@ -2085,10 +2320,20 @@ "@lezer/lr": "^1.0.0" } }, + "node_modules/@lezer/go": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@lezer/go/-/go-1.0.0.tgz", + "integrity": "sha512-co9JfT3QqX1YkrMmourYw2Z8meGC50Ko4d54QEcQbEYpvdUvN4yb0NBZdn/9ertgvjsySxHsKzH3lbm3vqJ4Jw==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, "node_modules/@lezer/highlight": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.1.6.tgz", - "integrity": "sha512-cmSJYa2us+r3SePpRCjN5ymCqCPv+zyXmDl0ciWtVaNiORT/MxM7ZgOMQZADD0o51qOaOg24qc/zBViOIwAjJg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.0.tgz", + "integrity": "sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==", "dependencies": { "@lezer/common": "^1.0.0" } @@ -2103,6 +2348,16 @@ "@lezer/lr": "^1.0.0" } }, + "node_modules/@lezer/java": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@lezer/java/-/java-1.1.1.tgz", + "integrity": "sha512-mt3dX13fRlpY7RlWELYRakanXgmwXsLRCrhstrn+c1sZd7jR2xle46/3heoxGd+oHxnuTnpoyXTyxcLJQs9+mQ==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, "node_modules/@lezer/javascript": { "version": "1.4.5", "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.5.tgz", @@ -2112,10 +2367,20 @@ "@lezer/lr": "^1.3.0" } }, + "node_modules/@lezer/json": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.2.tgz", + "integrity": "sha512-xHT2P4S5eeCYECyKNPhr4cbEL9tc8w83SPwRC373o9uEdrvGKTZoJVAGxpOsZckMlEh9W23Pc72ew918RWQOBQ==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, "node_modules/@lezer/lr": { - "version": "1.3.9", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.3.9.tgz", - "integrity": "sha512-XPz6dzuTHlnsbA5M2DZgjflNQ+9Hi5Swhic0RULdp3oOs3rh6bqGZolosVqN/fQIT8uNiepzINJDnS39oweTHQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.0.tgz", + "integrity": "sha512-Wst46p51km8gH0ZUmeNrtpRYmdlRHUpN1DQd3GFAyKANi8WVz8c2jHYTf1CVScFaCjQw1iO3ZZdqGDxQPRErTg==", "dependencies": { "@lezer/common": "^1.0.0" } @@ -2129,6 +2394,66 @@ "@lezer/highlight": "^1.0.0" } }, + "node_modules/@lezer/php": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@lezer/php/-/php-1.0.2.tgz", + "integrity": "sha512-GN7BnqtGRpFyeoKSEqxvGvhJQiI4zkgmYnDk/JIyc7H7Ifc1tkPnUn/R2R8meH3h/aBf5rzjvU8ZQoyiNDtDrA==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.1.0" + } + }, + "node_modules/@lezer/python": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/@lezer/python/-/python-1.1.13.tgz", + "integrity": "sha512-AdbRAtdQq94PfTNd4kqMEJhH2fqa2JdoyyqqVewY6w34w2Gi6dg2JuOtOgR21Bi0zP9r0KjSSHOUq/tP7FVT8A==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/rust": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@lezer/rust/-/rust-1.0.2.tgz", + "integrity": "sha512-Lz5sIPBdF2FUXcWeCu1//ojFAZqzTQNRga0aYv6dYXqJqPfMdCAI0NzajWUd4Xijj1IKJLtjoXRPMvTKWBcqKg==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/sass": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@lezer/sass/-/sass-1.0.6.tgz", + "integrity": "sha512-w/RCO2dIzZH1To8p+xjs8cE+yfgGus8NZ/dXeWl/QzHyr+TeBs71qiE70KPImEwvTsmEjoWh0A5SxMzKd5BWBQ==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/xml": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@lezer/xml/-/xml-1.0.5.tgz", + "integrity": "sha512-VFouqOzmUWfIg+tfmpcdV33ewtK+NSwd4ngSe1aG7HFb4BN0ExyY1b8msp+ndFrnlG4V4iC8yXacjFtrwERnaw==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/yaml": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@lezer/yaml/-/yaml-1.0.2.tgz", + "integrity": "sha512-XCkwuxe+eumJ28nA9e1S6XKsXz9W7V/AG+WBiWOtiIuUpKcZ/bHuvN8bLxSDREIcybSRpEd/jvphh4vgm6Ed2g==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.4.0" + } + }, "node_modules/@mdx-js/mdx": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.0.0.tgz", @@ -16261,14 +16586,6 @@ "inBundle": true, "license": "MIT" }, - "node_modules/npm/node_modules/depd": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/npm/node_modules/diff": { "version": "5.2.0", "dev": true, @@ -16420,17 +16737,6 @@ "inBundle": true, "license": "ISC" }, - "node_modules/npm/node_modules/has": { - "version": "1.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/npm/node_modules/has-unicode": { "version": "2.0.1", "dev": true, @@ -16595,11 +16901,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/ip": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, "node_modules/npm/node_modules/ip-address": { "version": "9.0.5", "dev": true, diff --git a/package.json b/package.json index 5a106e8f..b90a2ca4 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "license": "MIT", "dependencies": { "@codemirror/lang-markdown": "^6.2.3", + "@codemirror/language-data": "^6.5.1", "@codemirror/merge": "^6.4.0", "@codemirror/state": "^6.4.0", "@codemirror/view": "^6.23.0", diff --git a/src/examples/assets/code-blocks.md b/src/examples/assets/code-blocks.md index 003c0bd6..fea621f8 100644 --- a/src/examples/assets/code-blocks.md +++ b/src/examples/assets/code-blocks.md @@ -2,7 +2,7 @@ Blocks of code JavaScript: -```js +```jsx export default function App() { return

Hello world from a markdown

} diff --git a/src/examples/basics.tsx b/src/examples/basics.tsx index ba821946..dbc0cf8f 100644 --- a/src/examples/basics.tsx +++ b/src/examples/basics.tsx @@ -29,6 +29,7 @@ import codeBlocksMarkdown from './assets/code-blocks.md?raw' import imageMarkdown from './assets/image.md?raw' import jsxMarkdown from './assets/jsx.md?raw' import tableMarkdown from './assets/table.md?raw' +import { basicDark } from 'cm6-theme-basic-dark' import { virtuosoSampleSandpackConfig } from './_boilerplate' @@ -226,7 +227,10 @@ export function CodeBlock() { plugins={[ codeBlockPlugin({ codeBlockEditorDescriptors: [PlainTextCodeEditorDescriptor] }), sandpackPlugin({ sandpackConfig: virtuosoSampleSandpackConfig }), - codeMirrorPlugin({ codeBlockLanguages: { js: 'JavaScript', css: 'CSS' } }) + codeMirrorPlugin({ + codeBlockLanguages: { jsx: 'JavaScript (react)', js: 'JavaScript', css: 'CSS' } + // codeMirrorExtensions: [basicDark] + }) ]} /> ) diff --git a/src/plugins/codeblock/CodeBlockNode.tsx b/src/plugins/codeblock/CodeBlockNode.tsx index dfa1c2bb..f30f155c 100644 --- a/src/plugins/codeblock/CodeBlockNode.tsx +++ b/src/plugins/codeblock/CodeBlockNode.tsx @@ -168,33 +168,31 @@ const CodeBlockEditorContextProvider: React.FC<{ lexicalNode: CodeBlockNode children: React.ReactNode }> = ({ parentEditor, lexicalNode, children }) => { - return ( - { - parentEditor.update(() => { - lexicalNode.setCode(code) - setTimeout(() => { - parentEditor.dispatchCommand(NESTED_EDITOR_UPDATED_COMMAND, undefined) - }, 0) - }) - }, - setLanguage: (language: string) => { - parentEditor.update(() => { - lexicalNode.setLanguage(language) - }) - }, - setMeta: (meta: string) => { - parentEditor.update(() => { - lexicalNode.setMeta(meta) - }) - } - }} - > - {children} - - ) + const contextValue = React.useMemo(() => { + return { + lexicalNode, + setCode: (code: string) => { + parentEditor.update(() => { + lexicalNode.setCode(code) + setTimeout(() => { + parentEditor.dispatchCommand(NESTED_EDITOR_UPDATED_COMMAND, undefined) + }, 0) + }) + }, + setLanguage: (language: string) => { + parentEditor.update(() => { + lexicalNode.setLanguage(language) + }) + }, + setMeta: (meta: string) => { + parentEditor.update(() => { + lexicalNode.setMeta(meta) + }) + } + } + }, [lexicalNode, parentEditor]) + + return {children} } /** diff --git a/src/plugins/codemirror/CodeMirrorEditor.tsx b/src/plugins/codemirror/CodeMirrorEditor.tsx index 924e8775..4faa9fbd 100644 --- a/src/plugins/codemirror/CodeMirrorEditor.tsx +++ b/src/plugins/codemirror/CodeMirrorEditor.tsx @@ -1,43 +1,83 @@ -import { SandpackProvider, CodeEditor as TheEditorFromSandpack } from '@codesandbox/sandpack-react' import React from 'react' import styles from '../../styles/ui.module.css' import { CodeBlockEditorProps } from '../codeblock' import { useCodeBlockEditorContext } from '../codeblock/CodeBlockNode' import { readOnly$ } from '../core' -import { useCodeMirrorRef } from '../sandpack/useCodeMirrorRef' import { useCellValues } from '@mdxeditor/gurx' -import { codeMirrorTheme$ } from '.' + +import { EditorState, Extension } from '@codemirror/state' +import { EditorView, lineNumbers } from '@codemirror/view' +import { basicLight } from 'cm6-theme-basic-light' +import { basicSetup } from 'codemirror' +import { languages } from '@codemirror/language-data' +import { useCodeMirrorRef } from '../sandpack/useCodeMirrorRef' +import { codeMirrorExtensions$ } from '.' + +export const COMMON_STATE_CONFIG_EXTENSIONS: Extension[] = [] export const CodeMirrorEditor = ({ language, nodeKey, code, focusEmitter }: CodeBlockEditorProps) => { - const codeMirrorRef = useCodeMirrorRef(nodeKey, 'codeblock', 'jsx', focusEmitter) - const [readOnly, theme] = useCellValues(readOnly$, codeMirrorTheme$) + const [readOnly, codeMirrorExtensions] = useCellValues(readOnly$, codeMirrorExtensions$) + + const codeMirrorRef = useCodeMirrorRef(nodeKey, 'codeblock', language, focusEmitter) const { setCode } = useCodeBlockEditorContext() + const editorViewRef = React.useRef(null) + const elRef = React.useRef(null) + + const setCodeRef = React.useRef(setCode) + setCodeRef.current = setCode + codeMirrorRef.current = { + getCodemirror: () => editorViewRef.current! + } React.useEffect(() => { - codeMirrorRef.current?.getCodemirror()?.dom.addEventListener('paste', (e) => { - e.stopPropagation() - }) - }, [codeMirrorRef, language]) + void (async () => { + const extensions = [ + ...codeMirrorExtensions, + basicSetup, + basicLight, + lineNumbers(), + EditorView.lineWrapping, + EditorView.updateListener.of(({ state }) => { + setCodeRef.current(state.doc.toString()) + }) + ] + if (readOnly) { + extensions.push(EditorState.readOnly.of(true)) + } + if (language !== '') { + const languageData = languages.find((l) => { + return l.name === language || l.alias.includes(language) || l.extensions.includes(language) + }) + if (languageData) { + try { + const languageSupport = await languageData.load() + extensions.push(languageSupport.extension) + } catch (e) { + console.warn('failed to load language support for', language) + } + } + } + elRef.current!.innerHTML = '' + editorViewRef.current = new EditorView({ + parent: elRef.current!, + state: EditorState.create({ doc: code, extensions }) + }) + })() + return () => { + editorViewRef.current?.destroy() + editorViewRef.current = null + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [readOnly, language]) return (
{ e.stopPropagation() }} > - - - +
) } diff --git a/src/plugins/codemirror/index.tsx b/src/plugins/codemirror/index.tsx index 15bb712d..525dad47 100644 --- a/src/plugins/codemirror/index.tsx +++ b/src/plugins/codemirror/index.tsx @@ -2,7 +2,7 @@ import { realmPlugin } from '../../RealmWithPlugins' import { Cell, Signal, map } from '@mdxeditor/gurx' import { CodeBlockEditorDescriptor, appendCodeBlockEditorDescriptor$, insertCodeBlock$ } from '../codeblock' import { CodeMirrorEditor } from './CodeMirrorEditor' -import { SandpackThemeProp } from '@codesandbox/sandpack-react/types' +import { Extension } from '@codemirror/state' /** * The codemirror code block languages. @@ -37,13 +37,17 @@ export const insertCodeMirror$ = Signal<{ language: string; code: string }>((r) }) /** - * The theme CodeMirrorEditor used. - * It can be "light" | "dark" | "auto", - * or the theme in "@codesandbox/sandpack-themes" package, - * or you can also custom one, - * learn more https://sandpack.codesandbox.io/docs/getting-started/themes#custom-theme + * The code mirror extensions for the coemirror code block editor. + * @group CodeMirror */ -export const codeMirrorTheme$ = Cell('auto') +export const codeMirrorExtensions$ = Cell([]) + +/** + * Whether or not to try to dynamically load the code block language support. + * Disable if you want to manually pass the supported languages. + * @group CodeMirror + */ +export const codeMirrorAutoLoadLanguageSupport$ = Cell(true) /** * A plugin that adds lets users edit code blocks with CodeMirror. @@ -52,22 +56,30 @@ export const codeMirrorTheme$ = Cell('auto') export const codeMirrorPlugin = realmPlugin<{ codeBlockLanguages: Record /** - * The theme of CodeMirrorEditor + * Optional, additional CodeMirror extensions to load in the diff/source mode. + */ + codeMirrorExtensions?: Extension[] + /** + * Whether or not to try to dynamically load the code block language support. + * Disable if you want to manually pass the supported languages. + * @group CodeMirror */ - theme?: SandpackThemeProp + autoLoadLanguageSupport?: boolean }>({ update(r, params) { r.pubIn({ [codeBlockLanguages$]: params?.codeBlockLanguages, - [codeMirrorTheme$]: params?.theme || 'auto' + [codeMirrorExtensions$]: params?.codeMirrorExtensions || [], + [codeMirrorAutoLoadLanguageSupport$]: params?.autoLoadLanguageSupport ?? true }) }, init(r, params) { r.pubIn({ [codeBlockLanguages$]: params?.codeBlockLanguages, - [codeMirrorTheme$]: params?.theme || 'auto', - [appendCodeBlockEditorDescriptor$]: buildCodeBlockDescriptor(params?.codeBlockLanguages || {}) + [codeMirrorExtensions$]: params?.codeMirrorExtensions || [], + [appendCodeBlockEditorDescriptor$]: buildCodeBlockDescriptor(params?.codeBlockLanguages || {}), + [codeMirrorAutoLoadLanguageSupport$]: params?.autoLoadLanguageSupport ?? true }) } }) diff --git a/src/plugins/sandpack/useCodeMirrorRef.ts b/src/plugins/sandpack/useCodeMirrorRef.ts index 9bfec0c5..4eeea7fb 100644 --- a/src/plugins/sandpack/useCodeMirrorRef.ts +++ b/src/plugins/sandpack/useCodeMirrorRef.ts @@ -10,7 +10,7 @@ export function useCodeMirrorRef(nodeKey: string, editorType: 'codeblock' | 'san const activeEditor = useCellValue(activeEditor$) const setEditorInFocus = usePublisher(editorInFocus$) // const setActiveEditorType = usePublisher('activeEditorType') - const codeMirrorRef = React.useRef(null) + const codeMirrorRef = React.useRef(null) const { lexicalNode } = useCodeBlockEditorContext() // these flags escape the editor with arrows. @@ -90,7 +90,7 @@ export function useCodeMirrorRef(nodeKey: string, editorType: 'codeblock' | 'san setTimeout(() => { codeMirror?.getCodemirror()?.contentDOM?.addEventListener('focus', onFocusHandler) codeMirror?.getCodemirror()?.contentDOM?.addEventListener('keydown', onKeyDownHandler) - }, 100) + }, 300) return () => { codeMirror?.getCodemirror()?.contentDOM.removeEventListener('focus', onFocusHandler) diff --git a/src/styles/ui.module.css b/src/styles/ui.module.css index e49dac95..ec37e2ff 100644 --- a/src/styles/ui.module.css +++ b/src/styles/ui.module.css @@ -361,8 +361,12 @@ padding: var(--spacing-3); } -.sandpackWrapper { +.codeMirrorWrapper { margin-bottom: var(--spacing-5); + border: 1px solid var(--baseLine); + border-radius: var(--radius-medium); + overflow: hidden; + padding: 0.8rem; } .frontmatterWrapper { @@ -1178,7 +1182,7 @@ form.multiFieldForm { vertical-align: baseline; align-items: center; position: relative; - + &::after, & input { width: auto; @@ -1193,11 +1197,11 @@ form.multiFieldForm { border: none; color: inherit; } - + span { padding: 0.25em; } - + &::after { content: attr(data-value); white-space: pre-wrap;